diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4a76dc1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +.cvsignore export-ignore +docs/phpdoc/.empty export-ignore +thirdparty/simpletest/simpletest/docs/fr/server_stubs_documentation.html export-ignore +thirdparty/simpletest/simpletest/test/support/collector/collectable.2 export-ignore +thirdparty/simpletest/simpletest/test/support/collector/collectable.1 export-ignore +templates/ktcore/forms/widgets/info.smarty export-ignore +tests/util/ktutil/dataset1/a export-ignore +tests/util/ktutil/dataset1/f export-ignore +tests/util/ktutil/dataset1/y export-ignore +tests/util/ktutil/dataset1/b/q export-ignore +tests/util/ktutil/dataset1/b/t export-ignore +tests/util/ktutil/dataset1/b/w export-ignore diff --git a/about.php b/about.php index dc48327..f8c8d4c 100644 --- a/about.php +++ b/about.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/action.php b/action.php index 972b79c..4512d58 100644 --- a/action.php +++ b/action.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/admin.php b/admin.php index 16ed8e7..cb5ffa3 100644 --- a/admin.php +++ b/admin.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/.htaccess b/bin/.htaccess index 3d19990..e3f1013 100644 --- a/bin/.htaccess +++ b/bin/.htaccess @@ -1,4 +1,2 @@ -Order deny,allow -Allow from all - -IndexIgnore *.* +Order allow,deny +Allow from 127.0.0.1, localhost diff --git a/bin/automated_upgrade.php b/bin/automated_upgrade.php index 80b9c87..4a919b4 100644 --- a/bin/automated_upgrade.php +++ b/bin/automated_upgrade.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/checkopenoffice.php b/bin/checkopenoffice.php index 95033eb..2451ef5 100644 --- a/bin/checkopenoffice.php +++ b/bin/checkopenoffice.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/cleanup.php b/bin/cleanup.php index 1a10a3f..00cde57 100644 --- a/bin/cleanup.php +++ b/bin/cleanup.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/expungeall.php b/bin/expungeall.php index 581713e..949af56 100644 --- a/bin/expungeall.php +++ b/bin/expungeall.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/md5_validation.php b/bin/md5_validation.php index 8bdb891..ca6223b 100644 --- a/bin/md5_validation.php +++ b/bin/md5_validation.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/recreateIndexes.php b/bin/recreateIndexes.php index 37efbba..1f5d4e3 100644 --- a/bin/recreateIndexes.php +++ b/bin/recreateIndexes.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/scheduler.php b/bin/scheduler.php index 345a3b6..8ca0929 100644 --- a/bin/scheduler.php +++ b/bin/scheduler.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/storageverification.php b/bin/storageverification.php index 65766d1..fab1635 100644 --- a/bin/storageverification.php +++ b/bin/storageverification.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/system_info.php b/bin/system_info.php index bbaab85..e3409ef 100644 --- a/bin/system_info.php +++ b/bin/system_info.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/upgrade/pear-upgrade.bat b/bin/upgrade/pear-upgrade.bat deleted file mode 100644 index bb9fa80..0000000 --- a/bin/upgrade/pear-upgrade.bat +++ /dev/null @@ -1,27 +0,0 @@ - -; TEST ALL PEAR LIBRARIES BEFORE UPGRADING INTO RELEASE - -PATH=%PATH%;c:\php5\PEAR - -pear channel-update pear.php.net -pear config-set php_dir "C:\kt\kt.trunk\thirdparty\pear" - -pear config-set preferred_state stable - -pear upgrade --alldeps PEAR -pear upgrade --alldeps Cache_Lite -pear upgrade --alldeps Config -pear upgrade --alldeps DB -pear upgrade --alldeps File - -;pear upgrade --alldeps MDB2#mysql - -pear upgrade --alldeps Log -pear upgrade --alldeps PHP_Compat - -pear config-set preferred_state beta -pear upgrade --alldeps File_Gettext -pear upgrade --alldeps Net_LDAP -pear upgrade --alldeps SOAP -pear config-set preferred_state stable - diff --git a/bin/upgrade/pre-upgrade-3.0b3.php b/bin/upgrade/pre-upgrade-3.0b3.php index 31f0865..498f728 100644 --- a/bin/upgrade/pre-upgrade-3.0b3.php +++ b/bin/upgrade/pre-upgrade-3.0b3.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/upgrade/upgrade-to-2.0.6.php b/bin/upgrade/upgrade-to-2.0.6.php index 22da09a..0ffd455 100644 --- a/bin/upgrade/upgrade-to-2.0.6.php +++ b/bin/upgrade/upgrade-to-2.0.6.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/win32/installScheduler.php b/bin/win32/installScheduler.php index c280227..6ee8af7 100644 --- a/bin/win32/installScheduler.php +++ b/bin/win32/installScheduler.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/win32/schedulerService.php b/bin/win32/schedulerService.php index 15669f8..59ddc4f 100644 --- a/bin/win32/schedulerService.php +++ b/bin/win32/schedulerService.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/win32/schedulerServiceStatus.php b/bin/win32/schedulerServiceStatus.php index 509511e..bd8e805 100644 --- a/bin/win32/schedulerServiceStatus.php +++ b/bin/win32/schedulerServiceStatus.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/bin/win32/uninstallScheduler.php b/bin/win32/uninstallScheduler.php index 82adb9b..5143664 100644 --- a/bin/win32/uninstallScheduler.php +++ b/bin/win32/uninstallScheduler.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/browse.php b/browse.php index e7931f7..2907f44 100644 --- a/browse.php +++ b/browse.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/call_home.php b/call_home.php deleted file mode 100644 index 7520d41..0000000 --- a/call_home.php +++ /dev/null @@ -1,18 +0,0 @@ -||||| -*/ - -$data = isset($_REQUEST['system_info']) ? strip_tags($_REQUEST['system_info']) : ''; - -if(empty($data)){ - exit(0); -} - -$file = 'var/system_info.txt'; -$fp = fopen($file, 'a'); -fwrite($fp, $data."\n"); -fclose($fp); - -exit(0); -?> \ No newline at end of file diff --git a/config/dmsDefaults.php b/config/dmsDefaults.php index 5e79e0e..e0033d7 100644 --- a/config/dmsDefaults.php +++ b/config/dmsDefaults.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/config/siteMap.inc b/config/siteMap.inc index 7c395df..f6905fa 100644 --- a/config/siteMap.inc +++ b/config/siteMap.inc @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/config/tableMappings.inc b/config/tableMappings.inc index 0a78d93..3c6c6d8 100644 --- a/config/tableMappings.inc +++ b/config/tableMappings.inc @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/control.php b/control.php index 9355453..fb72518 100644 --- a/control.php +++ b/control.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -44,7 +44,6 @@ if(!$iu->isSystemInstalled()) { $iu->redirect("setup/wizard"); exit(0); } - // main library routines and defaults require_once('config/dmsDefaults.php'); diff --git a/customerrorpage.php b/customerrorpage.php index b8da3fc..b08d61b 100644 --- a/customerrorpage.php +++ b/customerrorpage.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/dashboard.php b/dashboard.php index 80e9c88..4b15d86 100644 --- a/dashboard.php +++ b/dashboard.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -135,9 +135,25 @@ class DashboardDispatcher extends KTStandardDispatcher { 'dashlets_left' => $aDashletsLeft, 'dashlets_right' => $aDashletsRight, ); + + // TODO : Is this ok? + if(file_exists(KT_DIR.DIRECTORY_SEPARATOR.'var'.DIRECTORY_SEPARATOR.'bin'.DIRECTORY_SEPARATOR."firstlogin.lock")) { + $this->runFirstLoginWizard($oTemplate, $aTemplateData); + } + return $oTemplate->render($aTemplateData); } + // + function runFirstLoginWizard($oTemplate, $aTemplateData) { + $this->oPage->requireCSSResource('setup/wizard/resources/css/modal.css'); + $this->oPage->requireJSResource('setup/wizard/resources/js/jquery-1.4.2.min.js'); + //$this->oPage->requireJSResource('thirdpartyjs/jquery/jquery-1.3.2.min.js'); + $this->oPage->requireJSResource('thirdpartyjs/jquery/jquery_noconflict.js'); + $this->oPage->requireJSResource('setup/wizard/resources/js/firstlogin.js'); + + } + // return some kind of ID for each dashlet // currently uses the class name function _getDashletId($oDashlet) { diff --git a/docs/COPYING b/docs/COPYING index 2bedb6b..20d894f 100644 --- a/docs/COPYING +++ b/docs/COPYING @@ -1,6 +1,6 @@ KnowledgeTree Community Edition Document Management Made Simple -Copyright (C) 2008, 2009 KnowledgeTree Inc. +Copyright (C) 2008-2010 KnowledgeTree Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the diff --git a/docs/VERSION-NAME.txt b/docs/VERSION-NAME.txt index 27d690f..fa1b5a0 100644 --- a/docs/VERSION-NAME.txt +++ b/docs/VERSION-NAME.txt @@ -1 +1 @@ -3.7.0.3 \ No newline at end of file +3.7.0.4 \ No newline at end of file diff --git a/docs/VERSION-OSS-DEV.txt b/docs/VERSION-OSS-DEV.txt index 27d690f..fa1b5a0 100644 --- a/docs/VERSION-OSS-DEV.txt +++ b/docs/VERSION-OSS-DEV.txt @@ -1 +1 @@ -3.7.0.3 \ No newline at end of file +3.7.0.4 \ No newline at end of file diff --git a/docs/VERSION-OSS.txt b/docs/VERSION-OSS.txt index 27d690f..fa1b5a0 100644 --- a/docs/VERSION-OSS.txt +++ b/docs/VERSION-OSS.txt @@ -1 +1 @@ -3.7.0.3 \ No newline at end of file +3.7.0.4 \ No newline at end of file diff --git a/docs/VERSION.txt b/docs/VERSION.txt index 27d690f..fa1b5a0 100644 --- a/docs/VERSION.txt +++ b/docs/VERSION.txt @@ -1 +1 @@ -3.7.0.3 \ No newline at end of file +3.7.0.4 \ No newline at end of file diff --git a/download_notification.php b/download_notification.php index 9281b8f..26b16d3 100644 --- a/download_notification.php +++ b/download_notification.php @@ -1,24 +1,43 @@ . + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * California 94120-7775, or email info@knowledgetree.com. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "Powered by + * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices + * must display the words "Powered by KnowledgeTree" and retain the original + * copyright notice. + * Contributor( s): ______________________________________ + * + */ + +/** + * + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Electronic Signatures diff --git a/examples/fieldsynchronisation/syncFieldFromLDAP.php b/examples/fieldsynchronisation/syncFieldFromLDAP.php index b160e00..1350e65 100644 --- a/examples/fieldsynchronisation/syncFieldFromLDAP.php +++ b/examples/fieldsynchronisation/syncFieldFromLDAP.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/help.php b/help.php index 291c82e..6a81d16 100644 --- a/help.php +++ b/help.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktapi/KTAPIAcl.inc.php b/ktapi/KTAPIAcl.inc.php index 0651b1e..009696d 100644 --- a/ktapi/KTAPIAcl.inc.php +++ b/ktapi/KTAPIAcl.inc.php @@ -1,12 +1,13 @@ $objectId, - 'comment' => $comment, - 'transactionNS' => $namespace, - 'userid' => $_SESSION['userID'], - 'ip' => Session::getClientIP(), - )); + $oDocumentTransaction = new DocumentTransaction($object, $comment, $namespace); + $res = $oDocumentTransaction->create(); break; + default: throw new Exception('Unexpected type: ' . $type); } diff --git a/ktapi/KTAPIBulkActions.inc.php b/ktapi/KTAPIBulkActions.inc.php index b13e584..4a5758f 100644 --- a/ktapi/KTAPIBulkActions.inc.php +++ b/ktapi/KTAPIBulkActions.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,11 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. - * - * @copyright 2008-2009, KnowledgeTree Inc. + * Contributor( s): ______________________________________ + */ + +/** + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTAPI diff --git a/ktapi/KTAPICollection.inc.php b/ktapi/KTAPICollection.inc.php index 64bb56c..fb8fb76 100644 --- a/ktapi/KTAPICollection.inc.php +++ b/ktapi/KTAPICollection.inc.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -33,8 +33,11 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. - * - * @copyright 2008-2009, KnowledgeTree Inc. + * Contributor( s): ______________________________________ + */ + +/** + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTAPI diff --git a/ktapi/KTAPIConstants.inc.php b/ktapi/KTAPIConstants.inc.php index f834149..e0c7409 100644 --- a/ktapi/KTAPIConstants.inc.php +++ b/ktapi/KTAPIConstants.inc.php @@ -4,36 +4,39 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * - * + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. - * + * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. - * + * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices - * must display the words "Powered by KnowledgeTree" and retain the original + * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. - * - * @copyright 2008-2009, KnowledgeTree Inc. + * Contributor( s): ______________________________________ + */ + +/** + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTAPI @@ -50,6 +53,7 @@ define('KTAPI_ERROR_SESSION_INVALID', 'The session could not be resolved.'); define('KTAPI_ERROR_PERMISSION_INVALID', 'The permission could not be resolved.'); define('KTAPI_ERROR_FOLDER_INVALID', 'The folder could not be resolved.'); define('KTAPI_ERROR_DOCUMENT_INVALID', 'The document could not be resolved.'); +define('KTAPI_ERROR_VERSION_INVALID', 'The document version could not be resolved.'); define('KTAPI_ERROR_USER_INVALID', 'The user could not be resolved.'); define('KTAPI_ERROR_KTAPI_INVALID', 'The ktapi could not be resolved.'); define('KTAPI_ERROR_INSUFFICIENT_PERMISSIONS', 'The user does not have sufficient permissions to access the resource.'); diff --git a/ktapi/KTAPIDocument.inc.php b/ktapi/KTAPIDocument.inc.php index fafff37..caa6e06 100644 --- a/ktapi/KTAPIDocument.inc.php +++ b/ktapi/KTAPIDocument.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,11 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. - * - * @copyright 2008-2009, KnowledgeTree Inc. + * Contributor( s): ______________________________________ + */ + +/** + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTAPI @@ -88,24 +91,30 @@ class KTAPI_Document extends KTAPI_FolderItem } /** - * This is used to get a document based on document id. + * This is used to get a document based on document id. Or a version of the document based on the metadata version id * * @author KnowledgeTree Team * @static * @access public * @param KTAPI $ktapi The ktapi object * @param int $documentid The document id + * @param int $iMetadataVersionId Optional. The metadata version id * @return KTAPI_Document The document object */ - function &get(&$ktapi, $documentid) + function &get(&$ktapi, $documentid, $iMetadataVersionId = null) { - assert(!is_null($ktapi)); - assert(is_a($ktapi, 'KTAPI')); - assert(is_numeric($documentid)); + if(is_null($ktapi) || !is_a($ktapi, 'KTAPI')){ + return PEAR::raiseError('A valid KTAPI object is needed'); + } + if(!is_numeric($documentid)){ + return PEAR::raiseError('A valid document id is required'); + } + + // ensure documentid is an integer $documentid += 0; - $document = &Document::get($documentid); + $document = &Document::get($documentid, $iMetadataVersionId); if (is_null($document) || PEAR::isError($document)) { return new KTAPI_Error(KTAPI_ERROR_DOCUMENT_INVALID,$document ); @@ -134,6 +143,30 @@ class KTAPI_Document extends KTAPI_FolderItem } /** + * This is used to get a document based on the document id and the metadata version + * + * @author KnowledgeTree Team + * @static + * @access public + * @param KTAPI $ktapi The ktapi object + * @param int $documentid The document id + * @param int $metadataVersion The metadata version (0,1,2) + * @return KTAPI_Document The document object + */ + function &get_by_metadata_version(&$ktapi, $documentid, $metadataVersion) + { + // get the metadata version id + $iMetadataVersionId = Document::getMetadataVersionIdFromVersion($documentid, $metadataVersion); + if (is_null($iMetadataVersionId) || PEAR::isError($iMetadataVersionId)) + { + return new KTAPI_Error(KTAPI_ERROR_VERSION_INVALID, $iMetadataVersionId ); + } + + // get the KTAPI_Document object + return self::get($ktapi, $documentid, $iMetadataVersionId); + } + + /** * Checks if a document has been deleted * * @author KnowledgeTree Team @@ -2108,6 +2141,18 @@ class KTAPI_Document extends KTAPI_FolderItem } /** + * Gets the content version id of the document + * + * @author KnowledgeTree Team + * @access public + * @return integer the content version id + */ + function get_content_version() + { + return $this->document->getContentVersionId(); + } + + /** * Gets the url which can be used to download the document. * * @param int $version Not implemented. The content version of the document @@ -2134,13 +2179,14 @@ class KTAPI_Document extends KTAPI_FolderItem * @author KnowledgeTree Team * @access public */ - function download() + function download($version = null) { $storage =& KTStorageManagerUtil::getSingleton(); $options = array(); + $comment = (!is_null($version)) ? 'Document version '.$version.' downloaded' : 'Document downloaded'; $oDocumentTransaction = new DocumentTransaction($this->document, 'Document downloaded', 'ktcore.transactions.download', $aOptions); - $oDocumentTransaction->create(); + return $oDocumentTransaction->create(); } /** @@ -2238,6 +2284,17 @@ class KTAPI_Document extends KTAPI_FolderItem } /** + * Get the content version id using the document (content) version - major/minor version + * + * @param string $version + * @return int + */ + function get_content_version_id_from_version($version) + { + return $this->document->getContentVersionIdFromVersion($version); + } + + /** * This expunges a document from the system. * * @@ -2557,10 +2614,13 @@ class KTAPI_Document extends KTAPI_FolderItem */ public function addDocumentToUserHistory() { - require_once(KT_DIR . '/plugins/commercial/network/userhistory/UserHistoryActions.php'); - - $docAction = new UserHistoryDocumentAction($this->document, $this->ktapi->get_user()); - $docAction->_show(); + if (KTPluginUtil::pluginIsActive('brad.UserHistory.plugin')) { + $path = KTPluginUtil::getPluginPath('brad.UserHistory.plugin'); + require_once($path . 'UserHistoryActions.php'); + + $docAction = new UserHistoryDocumentAction($this->document, $this->ktapi->get_user()); + $docAction->_show(); + } } /** @@ -2575,4 +2635,4 @@ class KTAPI_Document extends KTAPI_FolderItem } } -?> +?> \ No newline at end of file diff --git a/ktapi/KTAPIFolder.inc.php b/ktapi/KTAPIFolder.inc.php index c8daac0..c9bbe39 100755 --- a/ktapi/KTAPIFolder.inc.php +++ b/ktapi/KTAPIFolder.inc.php @@ -1,10 +1,11 @@ get_folder_by_name($foldername); + // TODO confirm that this addition of the parent folder id as second parameter is correct and necessary + $ktapi_folder = $this->get_folder_by_name($foldername, $this->folderid); } $currentFolderName = $this->get_folder_name(); @@ -391,7 +402,8 @@ class KTAPI_Folder extends KTAPI_FolderItem else { $foldername = substr($foldername, strlen($currentFolderName)+1); - $ktapi_folder = $this->get_folder_by_name($foldername); + // TODO confirm that this addition of the parent folder id as second parameter is correct and necessary + $ktapi_folder = $this->get_folder_by_name($foldername, $this->folderid); } } @@ -550,8 +562,8 @@ class KTAPI_Folder extends KTAPI_FolderItem foreach ($folder_children as $folder) { - - if(KTPermissionUtil::userHasPermissionOnItem($user, $folder_permission, $folder) + + if(KTPermissionUtil::userHasPermissionOnItem($user, $folder_permission, $folder) /*|| KTPermissionUtil::userHasPermissionOnItem($user, $read_permission, $folder)*/) { if ($depth-1 > 0) @@ -1594,7 +1606,7 @@ class KTAPI_Folder extends KTAPI_FolderItem return $_SESSION['errorMessage']; } - + /** * Method to add a Document to the User's History * @@ -1604,11 +1616,25 @@ class KTAPI_Folder extends KTAPI_FolderItem */ public function addFolderToUserHistory() { - require_once(KT_DIR . '/plugins/commercial/network/userhistory/UserHistoryActions.php'); - - $docAction = new UserHistoryFolderAction($this->folder, $this->ktapi->get_user()); - $docAction->_show(); + if (KTPluginUtil::pluginIsActive('brad.UserHistory.plugin')) { + $path = KTPluginUtil::getPluginPath('brad.UserHistory.plugin'); + require_once($path.'UserHistoryActions.php'); + + $folderAction = new UserHistoryFolderAction($this->folder, $this->ktapi->get_user()); + $folderAction->_show(); + } + } + + /** + * Method to get the Ids of all the Parent Folders + * + * @author KnowledgeTree Team + * @access public + */ + public function getParentFolderIDs() + { + return $this->folder->getParentFolderIDs(); } } -?> +?> \ No newline at end of file diff --git a/ktapi/KTAPISession.inc.php b/ktapi/KTAPISession.inc.php index 6890672..0d27155 100644 --- a/ktapi/KTAPISession.inc.php +++ b/ktapi/KTAPISession.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,11 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. - * - * @copyright 2008-2009, KnowledgeTree Inc. + * Contributor( s): ______________________________________ + */ + +/** + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTAPI diff --git a/ktapi/KTAPITrigger.inc.php b/ktapi/KTAPITrigger.inc.php index 6c632c2..804c2ec 100644 --- a/ktapi/KTAPITrigger.inc.php +++ b/ktapi/KTAPITrigger.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktapi/ktapi.inc.php b/ktapi/ktapi.inc.php index 6619201..33ea1e6 100644 --- a/ktapi/ktapi.inc.php +++ b/ktapi/ktapi.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTAPI @@ -842,11 +846,12 @@ class KTAPI * @author KnowledgeTree Team * @access public * @param string $foldername The folder name + * @param int $parent_id The folder in which to search for the requested child folder * @return object $folder The KTAPI_Folder object */ - public function &get_folder_by_name($foldername, $parentId = 1) + public function &get_folder_by_name($foldername, $parent_id = 1) { - $folder = KTAPI_Folder::_get_folder_by_name($this, $foldername, $parentId); + $folder = KTAPI_Folder::_get_folder_by_name($this, $foldername, $parent_id); return $folder; } @@ -865,6 +870,22 @@ class KTAPI } /** + * This returns a refererence to a document based on document id. + * + * @author KnowledgeTree Team + * @access public + * @param integer $documentid The document id + * @param integer $metadataVersion The metadata version of the document (not the id) + * @return object $document The KTAPI_Document object + */ + public function &get_document_by_metadata_version($documentid, $metadataVersion) + { + // Get the document using the metadata version + $document = KTAPI_Document::get_by_metadata_version($this, $documentid, $metadataVersion); + return $document; + } + + /** * This returns a document type id based on the name or an error object. * * @author KnowledgeTree Team @@ -2027,14 +2048,14 @@ class KTAPI function get_folder_shortcuts($folder_id) { $folder = $this->get_folder_by_id($folder_id); - if(PEAR::isError($folder)){ + if(PEAR::isError($folder)) { $response['status_code'] = 1; $response['message']= $folder->getMessage(); return $response; } $shortcuts = $folder->get_shortcuts(); - if(PEAR::isError($shortcuts)){ + if(PEAR::isError($shortcuts)) { $response['status_code'] = 1; $response['message']= $shortcuts->getMessage(); return $response; @@ -2054,10 +2075,10 @@ class KTAPI * @param string $folder_name The name of the folder * @return array Response 'results' contains kt_folder_detail | 'message' contains error message on failure */ - function get_folder_detail_by_name($folder_name) + function get_folder_detail_by_name($folder_name, $parent_id = 1) { - $folder = &$this->get_folder_by_name($folder_name); - if(PEAR::isError($folder)){ + $folder = &$this->get_folder_by_name($folder_name, $parent_id); + if(PEAR::isError($folder)) { $response['status_code'] = 1; $response['message']= $folder->getMessage(); return $response; @@ -2343,7 +2364,7 @@ class KTAPI $sourceName = $src_folder->get_folder_name(); $targetPath = $tgt_folder->get_full_path(); - $response['results'] = $this->get_folder_detail_by_name($targetPath . '/' . $sourceName); + $response['results'] = $this->get_folder_detail_by_name($targetPath . '/' . $sourceName, $source_id); return $response; } @@ -4714,21 +4735,28 @@ class KTAPI } return $response; } - + + /** + * Method to check whether content version is the latest for a specific document + * + * @author KnowledgeTree Team + * @access public + * @param string $documentID The id of the document + * @param string $contentID The id of the content version to check + * @return bool $response The formatted response array + */ public function is_latest_version($documentID, $contentID) - { - $sql = 'SELECT COUNT(document_content_version.id) AS newdocumentcount - FROM document_content_version - WHERE document_content_version.document_id ="'.$documentID.'" AND - document_content_version.id > "'.$contentID.'"'; + { + $document = $this->get_document_by_id($documentID); - $row = DBUtil::getOneResult($sql); - $row = (int)$row['newdocumentcount']; + $maxcontentID = $document->get_content_version(); - if ($row > 0) { + if ($maxcontentID > $contentID) { $response['is_latest'] = 'FALSE'; + $response['max_contentID'] = $maxcontentID; } else { $response['is_latest'] = 'TRUE'; + $response['max_contentID'] = $contentID; } $response['status_code'] = 0; @@ -4811,6 +4839,58 @@ class KTAPI return $response; } + + /** + * Method to get the Recently Viewed Documents + * + * @author KnowledgeTree Team + * @access public + */ + public function getRecentlyViewedDocuments() + { + if (KTPluginUtil::pluginIsActive('brad.UserHistory.plugin')) { + $path = KTPluginUtil::getPluginPath('brad.UserHistory.plugin'); + require_once($path.'UserHistoryActions.php'); + $user = $this->get_user(); + + if (is_null($user) || PEAR::isError($user)) + { + $result = new PEAR_Error(KTAPI_ERROR_USER_INVALID); + return $result; + } + + return UserHistoryDocumentEntry::getByUser($user); + + } else { + return array(); + } + } + + /** + * Method to get the Recently Viewed Folders + * + * @author KnowledgeTree Team + * @access public + */ + public function getRecentlyViewedFolders() + { + if (KTPluginUtil::pluginIsActive('brad.UserHistory.plugin')) { + $path = KTPluginUtil::getPluginPath('brad.UserHistory.plugin'); + require_once($path.'UserHistoryActions.php'); + $user = $this->get_user(); + + if (is_null($user) || PEAR::isError($user)) + { + $result = new PEAR_Error(KTAPI_ERROR_USER_INVALID); + return $result; + } + + return UserHistoryFolderEntry::getByUser($user); + + } else { + return array(); + } + } } diff --git a/ktwebdav/index.php b/ktwebdav/index.php index e40fd98..4aefbb5 100644 --- a/ktwebdav/index.php +++ b/ktwebdav/index.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebdav/ktwebdav.php b/ktwebdav/ktwebdav.php index 8b42235..5b15773 100644 --- a/ktwebdav/ktwebdav.php +++ b/ktwebdav/ktwebdav.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebdav/lib/KTWebDAVServer.inc.php b/ktwebdav/lib/KTWebDAVServer.inc.php index cb4d796..e8e4306 100644 --- a/ktwebdav/lib/KTWebDAVServer.inc.php +++ b/ktwebdav/lib/KTWebDAVServer.inc.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/KTDownloadManager.inc.php b/ktwebservice/KTDownloadManager.inc.php index cbe0075..5d32657 100644 --- a/ktwebservice/KTDownloadManager.inc.php +++ b/ktwebservice/KTDownloadManager.inc.php @@ -8,8 +8,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -76,44 +76,41 @@ class KTDownloadManager { $this->session = $session; } - + /** * This returns * * @access public * @param KTAPI_Document $document + * @param int $content_version_id Optional. The id of the requested content version * @return string */ - function allow_download($document, $content_version = null, $multipart = false) { + function allow_download($document, $content_version_id = null, $multipart = false) { assert ( ! is_null ( $document ) ); - - $content_version = 0; + $filesize = 0; - + if ($document instanceof KTAPI_Document) { $doc_id = $document->documentid; - $content_version = $document->document->getContentVersionId (); + //$content_version_id = (is_numeric($content_version_id)) ? $content_version_id : $document->document->getContentVersionId(); $filesize = $document->document->getFileSize (); } else if ($document instanceof Document || $document instanceof DocumentProxy) { $doc_id = $document->getId (); - $content_version = $document->getContentVersionId (); + //$content_version_id = (is_numeric($content_version_id)) ? $content_version_id : $document->getContentVersionId(); $filesize = $document->getFileSize (); } else if (is_numeric ( $document )) { $doc_id = $document; } else die ( 'gracefully' ); - - //assert(is_a($document, 'KTAPI_Document')); - - - $hash = sha1 ( "$doc_id $this->session $this->random" ); - - $id = DBUtil::autoInsert ( 'download_files', array ('document_id' => $doc_id, 'session' => $this->session, 'download_date' => date ( 'Y-m-d H:i:s' ), 'content_version' => $content_version, 'filesize' => $filesize, 'hash' => $hash ), array ('noid' => true ) ); - - return $multipart?$this->build_multipart_url( $hash, $doc_id ):$this->build_url ( $hash, $doc_id ); + + $hash = sha1 ( "$doc_id $content_version_id $this->session $this->random" ); + + $id = DBUtil::autoInsert ( 'download_files', array ('document_id' => $doc_id, 'session' => $this->session, 'download_date' => date ( 'Y-m-d H:i:s' ), 'content_version' => $content_version_id, 'filesize' => $filesize, 'hash' => $hash ), array ('noid' => true ) ); + + return $multipart ? $this->build_multipart_url( $hash, $doc_id ) : $this->build_url ( $hash, $doc_id ); } - - + + /** * This returns the url used to download a document. * @@ -125,21 +122,24 @@ class KTDownloadManager function build_url($hash, $documentid) { return $this->download_url . "?code=$hash&d=$documentid&u=$this->session"; } - + function build_multipart_url($hash, $documentId) { -// return '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'; return $this->multipart_download_url . "?code=$hash&d=$documentId&u=$this->session"; } - /** * This starts a download. * * @access public + * + * @param int $document_id + * @param string $hash + * @param string $apptype + * @return mixed */ - function download($document_id, $hash, $version = null, $apptype = 'ws') + function download($document_id, $hash, $apptype = 'ws') { - $sql = "SELECT 1 FROM download_files WHERE hash=? AND session=? AND document_id=?"; + $sql = "SELECT content_version FROM download_files WHERE hash=? AND session=? AND document_id=?"; $rows = DBUtil::getResultArray(array($sql, array($hash, $this->session, $document_id))); if (PEAR::isError($rows)) { @@ -151,11 +151,14 @@ class KTDownloadManager return new PEAR_Error('Invalid session.'); } + // Get the content version id + $content_version_id = $rows[0]['content_version']; + // If document is being downloaded by an external user bypass the session checking $check = strstr($this->session, 'ktext_'.$document_id); if($check == 0 && $check !== false){ // Use external download function - return $this->download_ext($document_id, $hash, $version = null); + return $this->download_ext($document_id, $hash, $content_version_id); } $storage =& KTStorageManagerUtil::getSingleton(); @@ -173,11 +176,9 @@ class KTDownloadManager return $document; } - if (!empty($version)) + if (!empty($content_version_id)) { - $version = KTDocumentContentVersion::get($version); - - $res = $storage->downloadVersion($document->document, $version); + $res = $storage->downloadVersion($document->document, $content_version_id); } else { @@ -193,8 +194,8 @@ class KTDownloadManager return true; } - - function download_ext($document_id, $hash, $version = null) + + function download_ext($document_id, $hash, $content_version_id = null) { $storage =& KTStorageManagerUtil::getSingleton(); $document = Document::get($document_id); @@ -203,11 +204,9 @@ class KTDownloadManager return $document; } - if (!empty($version)) + if (!empty($content_version_id)) { - $version = KTDocumentContentVersion::get($version); - - $res = $storage->downloadVersion($document, $version); + $res = $storage->downloadVersion($document, $content_version_id); } else { @@ -237,4 +236,4 @@ class KTDownloadManager DBUtil::runQuery($sql); } } -?> +?> \ No newline at end of file diff --git a/ktwebservice/KTUploadManager.inc.php b/ktwebservice/KTUploadManager.inc.php index 8cf64dc..46daf59 100644 --- a/ktwebservice/KTUploadManager.inc.php +++ b/ktwebservice/KTUploadManager.inc.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/KTWebService.php b/ktwebservice/KTWebService.php index a6936de..dfc1866 100644 --- a/ktwebservice/KTWebService.php +++ b/ktwebservice/KTWebService.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/download.php b/ktwebservice/download.php index 1a4a468..6965e84 100644 --- a/ktwebservice/download.php +++ b/ktwebservice/download.php @@ -6,8 +6,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -73,7 +73,7 @@ require_once('KTDownloadManager.inc.php'); $download_manager = new KTDownloadManager(); $download_manager->set_session($session); -$response = $download_manager->download($document_id, $hash, null, $apptype); +$response = $download_manager->download($document_id, $hash, $apptype); if (PEAR::isError($response)) { $msg = urlencode($response->getMessage()); diff --git a/ktwebservice/download_cleanup.php b/ktwebservice/download_cleanup.php index 5162c90..fb67915 100644 --- a/ktwebservice/download_cleanup.php +++ b/ktwebservice/download_cleanup.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/index.php b/ktwebservice/index.php index f295520..fbd61f5 100644 --- a/ktwebservice/index.php +++ b/ktwebservice/index.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/upload.php b/ktwebservice/upload.php index b164f70..e673634 100644 --- a/ktwebservice/upload.php +++ b/ktwebservice/upload.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/upload_cleanup.php b/ktwebservice/upload_cleanup.php index ec73749..9834810 100644 --- a/ktwebservice/upload_cleanup.php +++ b/ktwebservice/upload_cleanup.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/ktwebservice/webservice.php b/ktwebservice/webservice.php index e32b188..bfaafbc 100644 --- a/ktwebservice/webservice.php +++ b/ktwebservice/webservice.php @@ -8,7 +8,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -60,8 +60,6 @@ if (defined('HAS_SEARCH_FUNCTIONALITY')) require_once(KT_DIR . '/search2/search/search.inc.php'); } -// TODO: allow downloading of metadata versions -// TODO: allow downloading of document versions // TODO: chunking search results // TODO: add basic permissions management - add permissions to folder based on user/groups // TODO: refactor!!! download manager, split this file into a few smaller ones, etc @@ -72,7 +70,7 @@ if (defined('HAS_SEARCH_FUNCTIONALITY')) // TODO: ktwsapi/php must be made compatible with v2/v3 // TODO: subscriptions/notifications -// NOTE: some features are not implemented yet. most expected for v3. e.g. oem_document_no, custom_document_no, download($version)., get_metadata($version) +// NOTE: some features are not implemented yet. most expected for v3. e.g. oem_document_no, custom_document_no // Status Codes as defined in the specification. @@ -100,19 +98,19 @@ if (!defined('LATEST_WEBSERVICE_VERSION')) define('LATEST_WEBSERVICE_VERSION', 2); } - function bool2str($bool) +function bool2str($bool) +{ + if (is_bool($bool)) { - if (is_bool($bool)) - { - return $bool?'true':'false'; - } - if (is_numeric($bool)) - { - return ($bool+0)?'true':'false'; - } - // assume str - return (strtolower($bool) == 'true')?'true':'false'; + return $bool?'true':'false'; + } + if (is_numeric($bool)) + { + return ($bool+0)?'true':'false'; } + // assume str + return (strtolower($bool) == 'true')?'true':'false'; +} class KTWebService { @@ -769,13 +767,17 @@ class KTWebService // get_folder_detail_by_name $this->__dispatch_map['get_folder_detail_by_name'] = - array('in' => array('session_id' => 'string', 'folder_name' => 'string' ), + array('in' => array('session_id' => 'string', 'folder_name' => 'string'), 'out' => array('return' => "{urn:$this->namespace}kt_folder_detail"), ); if ($this->version >=3) { - $this->__dispatch_map['get_folder_detail_by_name']['in'] = array('session_id' => 'string', 'folder_id' => 'int', 'create'=>'boolean' ); + // NOTE that there was a bug: folder_id should be folder_name and be of type int - this is fixed in the second version below + // additionally the function has no "create" parameter + //$this->__dispatch_map['get_folder_detail_by_name']['in'] = array('session_id' => 'string', 'folder_id' => 'int', 'create'=>'boolean' ); + // now + $this->__dispatch_map['get_folder_detail_by_name']['in'] = array('session_id' => 'string', 'folder_name' => 'string', 'parent_id'=>'int' ); } // get_folder_contents @@ -1521,22 +1523,23 @@ class KTWebService * * @param string $session_id * @param string $folder_name + * @param integer $parent_id The parent folder in which to look for the named folder * @return kt_folder_detail. status_code can be KTWS_ERR_INVALID_SESSION, KTWS_ERR_INVALID_FOLDER, or KTWS_SUCCESS. */ - function get_folder_detail_by_name($session_id, $folder_name) + function get_folder_detail_by_name($session_id, $folder_name, $parent_id = 1) { - $this->debug("get_folder_detail_by_name('$session_id','$folder_name')"); + $this->debug("get_folder_detail_by_name('$session_id','$folder_name','$parent_id')"); $kt = &$this->get_ktapi($session_id); if (is_array($kt)) { return new SOAP_Value('return',"{urn:$this->namespace}kt_folder_detail", $kt); } - $folder = &$kt->get_folder_by_name($folder_name); + $folder = &$kt->get_folder_by_name($folder_name, $parent_id); if (PEAR::isError($folder)) { $response = KTWebService::_status(KTWS_ERR_INVALID_FOLDER,$folder); - $this->debug("get_folder_detail_by_name - cannot get folder $folder_name - " . $folder->getMessage(), $session_id); + $this->debug("get_folder_detail_by_name - cannot get folder $folder_name (looking in folder $parent_id) - " . $folder->getMessage(), $session_id); return new SOAP_Value('return',"{urn:$this->namespace}kt_folder_detail", $response); } @@ -1897,11 +1900,10 @@ class KTWebService if ($this->version >=2) { - $sourceName = $src_folder->get_folder_name(); $targetPath = $tgt_folder->get_full_path(); - $response = $this->get_folder_detail_by_name($session_id, $targetPath . '/' . $sourceName); + $response = $this->get_folder_detail_by_name($session_id, $targetPath . '/' . $sourceName, $source_id); return $response; } @@ -2940,12 +2942,13 @@ class KTWebService * * @param string $session_id * @param int $document_id + * @param string $version The document (content) version - "major version" . "minor version" * @return kt_response. status_code can be KTWS_ERR_INVALID_SESSION, KTWS_ERR_INVALID_DOCUMENT or KTWS_SUCCESS */ function download_document($session_id, $document_id, $version=null) { - $this->debug("download_document('$session_id',$document_id)"); + $this->debug("download_document('$session_id',$document_id, '$version')"); $kt = &$this->get_ktapi($session_id ); if (is_array($kt)) @@ -2964,11 +2967,23 @@ class KTWebService return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); } - $result = $document->download(); + $content_version_id = null; + if(!empty($version)){ + // Get the content version id for the given document version + $content_version_id = $document->get_content_version_id_from_version($version); + if (PEAR::isError($content_version_id)) + { + $response['message'] = $result->getMessage(); + $this->debug("download_document - cannot get version $version - " . $result->getMessage(), $session_id); + return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); + } + } + + $result = $document->download($version); if (PEAR::isError($result)) { $response['message'] = $result->getMessage(); - $this->debug("download_document - cannot download - " . $result->getMessage(), $session_id); + $this->debug("download_document - cannot download (version $version) - " . $result->getMessage(), $session_id); return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); } @@ -2976,7 +2991,7 @@ class KTWebService $download_manager = new KTDownloadManager(); $download_manager->set_session($session->session); $download_manager->cleanup(); - $url = $download_manager->allow_download($document); + $url = $download_manager->allow_download($document, $content_version_id); $response['status_code'] = KTWS_SUCCESS; $response['message'] = $url; @@ -3012,7 +3027,19 @@ class KTWebService return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); } - $result = $document->download(); + $content_version_id = null; + if(!empty($version)){ + // Get the content version id for the given document version + $content_version_id = $document->get_content_version_id_from_version($version); + if (PEAR::isError($content_version_id)) + { + $response['message'] = $result->getMessage(); + $this->debug("download_document - cannot get version $version - " . $result->getMessage(), $session_id); + return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); + } + } + + $result = $document->download($version); if (PEAR::isError($result)) { $response['message'] = $result->getMessage(); @@ -3020,24 +3047,33 @@ class KTWebService return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); } - $content=''; + $content = ''; + $oStorage =& KTStorageManagerUtil::getSingleton(); - $document = $document->document; + // for a specified version + if(is_numeric($content_version_id)){ + $filename = $oStorage->temporaryFileForVersion($content_version_id); - $oStorage =& KTStorageManagerUtil::getSingleton(); - $filename = $oStorage->temporaryFile($document); - - $fp=fopen($filename,'rb'); - if ($fp === false) - { + if(!$filename){ $response['message'] = 'The file is not in the storage system. Please contact an administrator!'; - $this->debug("download_small_document - cannot write $filename", $session_id); + $this->debug("download_small_document - $filename cannot be found in the storage system", $session_id); return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); - } - $content = fread($fp, filesize($filename)); - fclose($fp); - $content = base64_encode($content); + } + }else{ + $document = $document->document; + $filename = $oStorage->temporaryFile($document); + } + $fp=fopen($filename,'rb'); + if ($fp === false) + { + $response['message'] = 'The file is not in the storage system. Please contact an administrator!'; + $this->debug("download_small_document - cannot read $filename", $session_id); + return new SOAP_Value('return',"{urn:$this->namespace}kt_response", $response); + } + $content = fread($fp, filesize($filename)); + fclose($fp); + $content = base64_encode($content); $response['status_code'] = KTWS_SUCCESS; $response['message'] = $content; @@ -3739,9 +3775,9 @@ class KTWebService * @param int $document_id * @return kt_metadata_response */ - function get_document_metadata($session_id,$document_id) + function get_document_metadata($session_id, $document_id, $version = null) { - $this->debug("get_document_metadata('$session_id',$document_id)"); + $this->debug("get_document_metadata('$session_id',$document_id, $version)"); $kt = &$this->get_ktapi($session_id ); if (is_array($kt)) @@ -3751,7 +3787,12 @@ class KTWebService $response = KTWebService::_status(KTWS_ERR_INVALID_DOCUMENT); - $document = &$kt->get_document_by_id($document_id); + if(is_numeric($version)){ + $document = &$kt->get_document_by_metadata_version($document_id, $version); + }else { + $document = &$kt->get_document_by_id($document_id); + } + if (PEAR::isError($document)) { $response['message'] = $document->getMessage(); diff --git a/lib/actions/actionregistry.inc.php b/lib/actions/actionregistry.inc.php index cfa4c64..18d4348 100644 --- a/lib/actions/actionregistry.inc.php +++ b/lib/actions/actionregistry.inc.php @@ -4,8 +4,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/actions/bulkaction.php b/lib/actions/bulkaction.php index 9cf11e3..e3ee95a 100644 --- a/lib/actions/bulkaction.php +++ b/lib/actions/bulkaction.php @@ -5,8 +5,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/actions/documentaction.inc.php b/lib/actions/documentaction.inc.php index 2a372b0..2fe6d6b 100644 --- a/lib/actions/documentaction.inc.php +++ b/lib/actions/documentaction.inc.php @@ -4,8 +4,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/actions/documentviewlet.inc.php b/lib/actions/documentviewlet.inc.php index 0b50eb7..659fff6 100644 --- a/lib/actions/documentviewlet.inc.php +++ b/lib/actions/documentviewlet.inc.php @@ -5,8 +5,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/actions/entitylist.php b/lib/actions/entitylist.php index 979ab81..fc0c147 100644 --- a/lib/actions/entitylist.php +++ b/lib/actions/entitylist.php @@ -5,8 +5,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/actions/folderaction.inc.php b/lib/actions/folderaction.inc.php index 31ccd9c..ab3d699 100644 --- a/lib/actions/folderaction.inc.php +++ b/lib/actions/folderaction.inc.php @@ -4,8 +4,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/actions/portletregistry.inc.php b/lib/actions/portletregistry.inc.php index 7f2093e..be60606 100644 --- a/lib/actions/portletregistry.inc.php +++ b/lib/actions/portletregistry.inc.php @@ -4,8 +4,8 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. - * John + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. + * * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/lib/alert/delivery/EmailAlert.inc b/lib/alert/delivery/EmailAlert.inc index 9d5df8a..31ced1f 100644 --- a/lib/alert/delivery/EmailAlert.inc +++ b/lib/alert/delivery/EmailAlert.inc @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/lib/alert/delivery/SMSAlert.inc b/lib/alert/delivery/SMSAlert.inc index 978db71..5a69bc2 100644 --- a/lib/alert/delivery/SMSAlert.inc +++ b/lib/alert/delivery/SMSAlert.inc @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/lib/api/ktcmis/classes/CMISDocumentPropertyCollection.inc.php b/lib/api/ktcmis/classes/CMISDocumentPropertyCollection.inc.php index a6b9127..c746755 100644 --- a/lib/api/ktcmis/classes/CMISDocumentPropertyCollection.inc.php +++ b/lib/api/ktcmis/classes/CMISDocumentPropertyCollection.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008,2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. + * Contributor( s): ______________________________________ + */ + +/** * - * @copyright 2008-2009, KnowledgeTree Inc. + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS @@ -47,35 +51,35 @@ require_once(CMIS_DIR . '/classes/CMISPropertyCollection.inc.php'); */ class CMISDocumentPropertyCollection extends CMISPropertyCollection { - static $Name; - static $IsImmutable; - static $IsLatestVersion; - static $IsMajorVersion; - static $IsLatestMajorVersion; - static $VersionLabel; - static $VersionSeriesId; - static $IsVersionSeriesCheckedOut; - static $VersionSeriesCheckedOutBy; - static $VersionSeriesCheckedOutId; - static $CheckinComment; - static $ContentStreamLength; - static $ContentStreamMimeType; - static $ContentStreamFilename; - static $ContentStreamUri; + static $name; + static $isImmutable; + static $isLatestVersion; + static $isMajorVersion; + static $isLatestMajorVersion; + static $versionLabel; + static $versionSeriesId; + static $isVersionSeriesCheckedOut; + static $versionSeriesCheckedOutBy; + static $versionSeriesCheckedOutId; + static $checkinComment; + static $contentStreamLength; + static $contentStreamMimeType; + static $contentStreamFilename; + static $contentStreamUri; function __construct() { parent::__construct(); - self::$propertyTypes = array_merge(self::$propertyTypes, array('ContentStreamAllowed' => 'propertyString', - 'ContentStreamLength' => 'propertyInteger', - 'ContentStreamMimeType' => 'propertyString', - 'ContentStreamFilename' => 'propertyString', - 'ContentStreamUri' => 'propertyUri', - 'IsLatestVersion' => 'propertyBoolean', - 'IsVersionSeriesCheckedOut' => 'propertyBoolean', - 'VersionSeriesCheckedOutBy' => 'propertyString', - 'VersionSeriesCheckedOutId' => 'propertyId', - 'VersionLabel' => 'propertyString')); + self::$propertyTypes = array_merge(self::$propertyTypes, array('contentStreamAllowed' => 'propertyString', + 'contentStreamLength' => 'propertyInteger', + 'contentStreamMimeType' => 'propertyString', + 'contentStreamFilename' => 'propertyString', + 'contentStreamUri' => 'propertyUri', + 'isLatestVersion' => 'propertyBoolean', + 'isVersionSeriesCheckedOut' => 'propertyBoolean', + 'versionSeriesCheckedOutBy' => 'propertyString', + 'versionSeriesCheckedOutId' => 'propertyId', + 'versionLabel' => 'propertyString')); } } diff --git a/lib/api/ktcmis/classes/CMISFolderPropertyCollection.inc.php b/lib/api/ktcmis/classes/CMISFolderPropertyCollection.inc.php index be8cf23..dad955a 100644 --- a/lib/api/ktcmis/classes/CMISFolderPropertyCollection.inc.php +++ b/lib/api/ktcmis/classes/CMISFolderPropertyCollection.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008,2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. + * Contributor( s): ______________________________________ + */ + +/** * - * @copyright 2008-2009, KnowledgeTree Inc. + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS @@ -47,9 +51,9 @@ require_once(CMIS_DIR . '/classes/CMISPropertyCollection.inc.php'); */ class CMISFolderPropertyCollection extends CMISPropertyCollection { - static $Name; - static $ParentId; - static $AllowedChildObjectTypeIds; + static $name; + static $parentId; + static $allowedChildObjectTypeIds; function __construct() { diff --git a/lib/api/ktcmis/classes/CMISObject.inc.php b/lib/api/ktcmis/classes/CMISObject.inc.php index fd18b78..63d2874 100644 --- a/lib/api/ktcmis/classes/CMISObject.inc.php +++ b/lib/api/ktcmis/classes/CMISObject.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008,2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,30 +32,45 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. + * Contributor( s): ______________________________________ + */ + +/** * - * @copyright 2008-2009, KnowledgeTree Inc. + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS * @version Version 0.1 */ -abstract class CMISObject { +// NOTE designation "opaque" means the value may not be changed + +// TODO consider attributes as a class similar to property definitions, in order to more easily extract without having +// to have attribute specific code - protected $typeId; - protected $queryName; - protected $displayName; - protected $baseType; - protected $baseTypeQueryName; - protected $parentId; - protected $description; - protected $creatable; - protected $fileable; - protected $queryable; - protected $includedInSupertypeQuery; - protected $controllable; // NOTE deprecated? part of policy objects specification, policy objects are indicated as TODO remove - protected $contentStreamAllowed = 'notAllowed'; +abstract class CMISObject { + protected $id; // ID (opaque); identifies the object-type in the repository + protected $localName; // String (opaque, optional); local name for object-type - need not be set + protected $localNamespace; // String (opaque, optional); optional local namespace for object-type - need not be set + // NOTE queryName should not contain characters that negatively interact with BNF grammar + protected $queryName; // String (opaque); used for query and filter operations on object-types + protected $displayName; // String (optional); used for presentation by application + protected $baseId; // Enum; indicates base type + protected $parentId; // ID; id of immediate parent type; must be "not set" for a base type (Document, Folder, Relationship, Policy) + protected $description; // String (optional); used for presentation by application + protected $creatable; // Boolean; indicates whether new objects of this type may be created + protected $fileable; // Boolean; indicates whether objects of this type are fileable + protected $queryable; // Boolean; indicates whether this object-type can appear inthe FROM clause of a query statement + protected $controllablePolicy; // Boolean; indicates whether objects of this type are controllable via policies + protected $controllableACL; // Boolean; indicates whether objects of this type are controllable by ACLs + protected $fulltextIndexed; // Boolean; indicates whether objects of this type are indexed for full-text search + // for querying via the CONTAINS() query predicate + protected $includedInSupertypeQuery; // Boolean; indicates whether this type and sub-types appear in a query of this type's ancestor types + // For example: if Invoice is a sub-type of cmis:document, if this is TRUE on Invoice then for a query + // 391 on cmis:document, instances of Invoice will be returned if they match. + protected $properties; // list of property objects which define the additional properties for this object // TODO all we have here so far is getAttributes & getProperties @@ -63,6 +78,9 @@ abstract class CMISObject { public function __construct() { + // set properties shared by all objects of this type + $this->_setSharedProperties(); + // $propertyDef = new PropertyDefinition(); // $this->properties[] = $propertyDef; } @@ -78,18 +96,21 @@ abstract class CMISObject { // TODO look at how chemistry does this and implement something similar // for now this is fine as we are just trying to get things up and running :) - $attributes['typeId'] = $this->typeId; + $attributes['id'] = $this->id; + $attributes['localName'] = $this->localName; + $attributes['localNamespace'] = $this->localNamespace; $attributes['queryName'] = $this->queryName; $attributes['displayName'] = $this->displayName; - $attributes['baseType'] = $this->baseType; - $attributes['baseTypeQueryName'] = $this->baseTypeQueryName; + $attributes['baseId'] = $this->baseId; $attributes['parentId'] = $this->parentId; $attributes['description'] = $this->description; $attributes['creatable'] = $this->creatable; $attributes['fileable'] = $this->fileable; $attributes['queryable'] = $this->queryable; + $attributes['controllablePolicy'] = $this->controllablePolicy; + $attributes['controllableACL'] = $this->controllableACL; + $attributes['fulltextIndexed'] = $this->fulltextIndexed; $attributes['includedInSupertypeQuery'] = $this->includedInSupertypeQuery; - $attributes['controllable'] = $this->includedInSupertypeQuery; return $attributes; } @@ -132,15 +153,25 @@ abstract class CMISObject { return $this->properties->getValue($property); } - public function reload($documentId) + public function reload($objectId) { - $this->_get($documentId); + $this->_get($objectId); } - private function _get($documentId) + protected function _get($objectId) { // override in child classes } + + /** + * Sets properties which are shared between all objects of this type + */ + protected function _setSharedProperties() + { + $this->_setPropertyInternal('objectTypeId', strtolower($this->getAttribute('id'))); + // Needed to distinguish type + $this->_setPropertyInternal('baseTypeId', strtolower($this->getAttribute('id'))); + } } diff --git a/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php b/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php index 1f93815..48c2765 100644 --- a/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php +++ b/lib/api/ktcmis/classes/CMISPropertyCollection.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008,2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. + * Contributor( s): ______________________________________ + */ + +/** * - * @copyright 2008-2009, KnowledgeTree Inc. + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS @@ -45,41 +49,41 @@ */ abstract class CMISPropertyCollection { - static $ObjectId; - static $BaseType; - static $Uri; - static $ObjectTypeId; - static $CreatedBy; - static $CreationDate; - static $LastModifiedBy; - static $LastModificationDate; - static $ChangeToken; + static $objectId; + static $baseTypeId; + static $uri; + static $objectTypeId; + static $createdBy; + static $creationDate; + static $lastModifiedBy; + static $lastModificationDate; + static $changeToken; // TODO these definitions belong in their own classe definition (see property type definions,) but here will do for now static public $propertyTypes; - function __construct() + public function __construct() { - self::$propertyTypes = array('ObjectId' => 'propertyId', - 'Author' => 'propertyString', - 'BaseType' => 'propertyString', - 'ObjectTypeId' => 'propertyId', - 'CreatedBy' => 'propertyString', - 'CreationDate' => 'propertyDateTime', - 'LastModifiedBy' => 'propertyString', - 'LastModificationDate' => 'propertyDateTime', - 'Name' => 'propertyString', - 'Uri' => 'propertyUri', - 'AllowedChildObjectTypeIds' => 'propertyId', - 'CreatedBy' => 'propertyString', - 'CreationDate' => 'propertyDateTime', - 'ChangeToken' => 'propertyString', - 'ParentId' => 'propertyId'); + self::$propertyTypes = array('objectId' => 'propertyId', + 'author' => 'propertyString', + 'baseTypeId' => 'propertyId', + 'objectTypeId' => 'propertyId', + 'createdBy' => 'propertyString', + 'creationDate' => 'propertyDateTime', + 'lastModifiedBy' => 'propertyString', + 'lastModificationDate' => 'propertyDateTime', + 'name' => 'propertyString', + 'uri' => 'propertyUri', + 'allowedChildObjectTypeIds' => 'propertyId', + 'createdBy' => 'propertyString', + 'creationDate' => 'propertyDateTime', + 'changeToken' => 'propertyString', + 'parentId' => 'propertyId'); } /** * Gets the property value. */ - function getValue($field) + public function getValue($field) { return $this->{$field}; } @@ -88,12 +92,12 @@ abstract class CMISPropertyCollection { * Sets the property value. */ // for connection-tied live objects - function setValue($field, $value) + public function setValue($field, $value) { $this->{$field} = $value; } - function getFieldType($field) + public function getFieldType($field) { return $this->propertyTypes[$field]; } diff --git a/lib/api/ktcmis/classes/CMISRepository.inc.php b/lib/api/ktcmis/classes/CMISRepository.inc.php index db4390f..d2b8ff5 100644 --- a/lib/api/ktcmis/classes/CMISRepository.inc.php +++ b/lib/api/ktcmis/classes/CMISRepository.inc.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -33,8 +33,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS @@ -80,6 +84,7 @@ class CMISRepository { foreach($xml->repository as $repository) { $currentRepo = $repository->repositoryInfo[0]->repositoryId; + // TODO this is no longer correct - is an object of SimpleXMLElement and not a string or int if ((int)$currentRepo == $this->repositoryId) { $config = $repository; diff --git a/lib/api/ktcmis/classes/CMISRepositoryCapabilities.inc.php b/lib/api/ktcmis/classes/CMISRepositoryCapabilities.inc.php index 7d23077..0beea23 100644 --- a/lib/api/ktcmis/classes/CMISRepositoryCapabilities.inc.php +++ b/lib/api/ktcmis/classes/CMISRepositoryCapabilities.inc.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008,2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -33,8 +33,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. + * Contributor( s): ______________________________________ + */ + +/** * - * @copyright 2008-2009, KnowledgeTree Inc. + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS @@ -42,20 +46,34 @@ */ class CMISRepositoryCapabilities { - - // boolean values - protected $capabilityMultifiling; - protected $capabilityUnfiling; - protected $capabilityVersionSpecificFiling; - protected $capabilityPWCUpdateable; - protected $capabilityPWCSearchable; - protected $capabilityAllVersionsSearchable; - - // non-boolean values - // TODO these should be defined as classes/enums which will only accept the defined values when set - protected $capabilityQuery; - protected $capabilityFullText; - protected $capabilityJoin; + + // TODO we need an enum equivalent class which can be used to define acceptable values for all which are not boolean + + // navigation capabilities + protected $capabilityGetDescendants; // true/false + protected $capabilityGetFolderTree; // true/false + + // object capabilities + protected $capabilityContentStreamUpdatability; // none/anytime/pwconly + protected $capabilityChanges; // none/objectidsonly/properties/all + protected $capabilityRenditions; // none/read + + // filing capabilities + protected $capabilityMultifiling; // true/false + protected $capabilityUnfiling; // true/false + protected $capabilityVersionSpecificFiling; // true/false + + // versioning capabilities + protected $capabilityPWCUpdateable; // true/false + protected $capabilityPWCSearchable; // true/false + protected $capabilityAllVersionsSearchable; // true/false + + // query capabilities + protected $capabilityQuery; // none/metadataonly/fulltextonly/bothseparate/bothcombined + protected $capabilityJoin; // none/inneronly/innerandouter + + // acl capabilities + protected $capabilityACL; // none/discover/manage /** * Set a single field value @@ -63,90 +81,105 @@ class CMISRepositoryCapabilities { * @param string $field * @param string/int $value * @return a collection of repository entries + * + * TODO when we have the enum class in place we will need to check whether the value is of type enum and call its set function + * to ensure that the rules are followed */ function setFieldValue($field, $value) { $this->{$field} = ($value == 'true' ? true : ($value == 'false' ? false : $value)); } - + /** * Gets the value of the capabilityMultifiling property. * */ - public function hasCapabilityMultifiling() { - return $this->capabilityMultifiling; + public function hasCapabilityGetDescendants() { + return $this->capabilityGetDescendants; } - + /** - * Sets the value of the capabilityMultifiling property. + * Gets the value of the capabilityMultifiling property. * */ - public function setCapabilityMultifiling($value) { - $this->capabilityMultifiling = $value; + public function hasCapabilityGetFolderTree() { + return $this->capabilityGetFolderTree; } - + /** - * Gets the value of the capabilityUnfiling property. + * Gets the value of the capabilityContentStreamUpdatability property. + * + * @return + * possible object is + * {@link EnumCapabilityContentStreamUpdatability } * */ - public function hasCapabilityUnfiling() { - return $this->capabilityUnfiling; + public function getCapabilityContentStreamUpdatability() { + return $this->capabilityContentStreamUpdatability; } - + /** - * Sets the value of the capabilityUnfiling property. + * Gets the value of the capabilityChanges property. + * + * @return + * possible object is + * {@link EnumCapabilityChanges } * */ - public function setCapabilityUnfiling($value) { - $this->capabilityUnfiling = $value; + public function getCapabilityChanges() { + return $this->capabilityChanges; } - + /** - * Gets the value of the capabilityVersionSpecificFiling property. + * Gets the value of the capabilityRenditions property. + * + * @return + * possible object is + * {@link EnumCapabilityRenditions } * */ - public function hasCapabilityVersionSpecificFiling() { - return $this->capabilityVersionSpecificFiling; + public function getCapabilityRenditions() { + return $this->capabilityRenditions; } - + /** - * Sets the value of the capabilityVersionSpecificFiling property. + * Gets the value of the capabilityMultifiling property. * */ - public function setCapabilityVersionSpecificFiling($value) { - $this->capabilityVersionSpecificFiling = $value; + public function hasCapabilityMultifiling() { + return $this->capabilityMultifiling; } /** - * Gets the value of the capabilityPWCUpdateable property. + * Gets the value of the capabilityUnfiling property. * */ - public function hasCapabilityPWCUpdateable() { - return $this->capabilityPWCUpdateable; + public function hasCapabilityUnfiling() { + return $this->capabilityUnfiling; } - + /** - * Sets the value of the capabilityPWCUpdateable property. + * Gets the value of the capabilityVersionSpecificFiling property. * */ - public function setCapabilityPWCUpdateable($value) { - $this->capabilityPWCUpdateable = $value; + public function hasCapabilityVersionSpecificFiling() { + return $this->capabilityVersionSpecificFiling; } /** - * Gets the value of the capabilityPWCSearchable property. + * Gets the value of the capabilityPWCUpdateable property. * */ - public function hasCapabilityPWCSearchable() { - return $this->capabilityPWCSearchable; + public function hasCapabilityPWCUpdateable() { + return $this->capabilityPWCUpdateable; } /** - * Sets the value of the capabilityPWCSearchable property. + * Gets the value of the capabilityPWCSearchable property. * */ - public function setCapabilityPWCSearchable($value) { - $this->capabilityPWCSearchable = $value; + public function hasCapabilityPWCSearchable() { + return $this->capabilityPWCSearchable; } /** @@ -156,15 +189,7 @@ class CMISRepositoryCapabilities { public function hasCapabilityAllVersionsSearchable() { return $this->capabilityAllVersionsSearchable; } - - /** - * Sets the value of the capabilityAllVersionsSearchable property. - * - */ - public function setCapabilityAllVersionsSearchable($value) { - $this->capabilityAllVersionsSearchable = $value; - } - + /** * Gets the value of the capabilityQuery property. * @@ -178,18 +203,6 @@ class CMISRepositoryCapabilities { } /** - * Sets the value of the capabilityQuery property. - * - * @param value - * allowed object is - * {@link EnumCapabilityQuery } - * - */ - public function setCapabilityQuery($value) { - $this->capabilityQuery = $value; - } - - /** * Gets the value of the capabilityJoin property. * * @return @@ -202,39 +215,15 @@ class CMISRepositoryCapabilities { } /** - * Sets the value of the capabilityJoin property. - * - * @param value - * allowed object is - * {@link EnumCapabilityJoin } - * - */ - public function setCapabilityJoin($value) { - $this->capabilityJoin = $value; - } - - /** - * Gets the value of the capabilityFullText property. + * Gets the value of the capabilityACL property. * * @return * possible object is - * {@link EnumCapabilityFullText } - * - */ - public function getCapabilityFullText() { - return $this->capabilityFullText; - } - - /** - * Sets the value of the capabilityFullText property. - * - * @param value - * allowed object is - * {@link EnumCapabilityFullText } + * {@link EnumCapabilityACL } * */ - public function setCapabilityFullText($value) { - $this->capabilityFullText = $value; + public function getCapabilityACL() { + return $this->capabilityACL; } // /** diff --git a/lib/api/ktcmis/classes/CMISRepositoryInfo.inc.php b/lib/api/ktcmis/classes/CMISRepositoryInfo.inc.php index dd715d0..e203e57 100644 --- a/lib/api/ktcmis/classes/CMISRepositoryInfo.inc.php +++ b/lib/api/ktcmis/classes/CMISRepositoryInfo.inc.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008,2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -33,8 +33,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. + * Contributor( s): ______________________________________ + */ + +/** * - * @copyright 2008-2009, KnowledgeTree Inc. + * @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package KTCMIS diff --git a/lib/api/ktcmis/classes/Enum.inc.php b/lib/api/ktcmis/classes/Enum.inc.php new file mode 100644 index 0000000..9b1c01c --- /dev/null +++ b/lib/api/ktcmis/classes/Enum.inc.php @@ -0,0 +1,47 @@ + \ No newline at end of file diff --git a/lib/api/ktcmis/config/repositories.xml b/lib/api/ktcmis/config/repositories.xml index 14f590f..f88bfda 100644 --- a/lib/api/ktcmis/config/repositories.xml +++ b/lib/api/ktcmis/config/repositories.xml @@ -4,8 +4,7 @@ Document : repositories.xml Created on : 25 May 2009, 10:33 AM Author : KnowledgeTree Team - Description: - Contains definitions for all repositories accessible via the CMIS API. + Description: Contains definitions for all repositories accessible via the CMIS API. --> + + + + templates + complete + + \ No newline at end of file diff --git a/setup/firstlogin/config/community_config.xml b/setup/firstlogin/config/community_config.xml new file mode 100644 index 0000000..1b91e4c --- /dev/null +++ b/setup/firstlogin/config/community_config.xml @@ -0,0 +1,15 @@ + + + + + + + templates + complete + + \ No newline at end of file diff --git a/setup/firstlogin/config/config.xml b/setup/firstlogin/config/config.xml new file mode 100644 index 0000000..46e0ae9 --- /dev/null +++ b/setup/firstlogin/config/config.xml @@ -0,0 +1,15 @@ + + + + + + + templates + complete + + \ No newline at end of file diff --git a/setup/firstlogin/firstlogin.php b/setup/firstlogin/firstlogin.php new file mode 100644 index 0000000..0b73dc9 --- /dev/null +++ b/setup/firstlogin/firstlogin.php @@ -0,0 +1,698 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package Fist Start Wizard +* @version Version 0.1 +*/ + +class firstlogin { + /** + * Reference to simple xml object + * + * @author KnowledgeTree Team + * @access protected + * @var object SimpleXMLElement + */ + protected $simpleXmlObj = null; + + /** + * Reference to step action object + * + * @author KnowledgeTree Team + * @access protected + * @var object StepAction + */ + protected $stepAction = null; + + /** + * Reference to session object + * + * @author KnowledgeTree Team + * @access protected + * @var object Session + */ + protected $session = null; + + /** + * List of steps as strings + * + * @author KnowledgeTree Team + * @access protected + * @var array string + */ + protected $stepClassNames = array(); + + /** + * List of steps as human readable strings + * + * @author KnowledgeTree Team + * @access protected + * @var array string + */ + protected $stepNames = array(); + + /** + * List of steps as human readable strings + * + * @author KnowledgeTree Team + * @access protected + * @var array string + */ + protected $stepObjects = array(); + + /** + * Order in which steps have to be done + * + * @author KnowledgeTree Team + * @access protected + * @var array string + */ + protected $orders = array(); + + /** + * List of properties + * + * @author KnowledgeTree Team + * @access protected + * @var array string + */ + protected $properties = array(); + + /** + * Flag if a step object needs confirmation + * + * @author KnowledgeTree Team + * @access protected + * @var boolean + */ + protected $stepConfirmation = false; + + /** + * Flag if a step object needs confirmation + * + * @author KnowledgeTree Team + * @access protected + * @var boolean + */ + protected $stepDisplayFirst = false; + + private $action = ''; + + /** + * Constructs object + * + * @author KnowledgeTree Team + * @access public + * @param object Session $session Instance of the Session object + */ + public function __construct($session = null) { + $this->session = $session; + } + + /** + * Read xml configuration file + * + * @author KnowledgeTree Team + * @param string $name of config file + * @access private + * @return object + */ + private function _readXml($name = "config.xml") { + try { + $this->simpleXmlObj = simplexml_load_file(CONF_DIR.INSTALL_TYPE."_$name"); + } catch (Exception $e) { + $util = new firstloginUtil(); + $util->error("Error reading configuration file: $e"); + exit(); + } + } + + /** + * Checks if first step + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return boolean + */ + private function _firstStep() { + if(isset($_GET['step_name'])) { + return false; + } + + return true; + } + + /** + * Checks if first step + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return boolean + */ + private function _firstStepPeriod() { + if(isset($_GET['step_name'])) { + if($_GET['step_name'] != 'installation') + return false; + } + + return true; + } + + /** + * Returns next step + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return string + */ + private function _getNextStep() { + return $this->_getStepName(1); + } + + /** + * Returns previous step + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return string + */ + private function _getPreviousStep() { + return $this->_getStepName(-1); + } + + /** + * Returns the step name, given a position + * + * @author KnowledgeTree Team + * @param integer $pos current position + * @access private + * @return string $name + */ + private function _getStepName($pos = 0) { + if($this->_firstStep()) { + $step = (string) $this->simpleXmlObj->steps->step[0]; + } else { + $pos += $this->getStepPosition(); + $step = (string) $this->simpleXmlObj->steps->step[$pos]; + } + + return $step; + } + + /** + * Executes next step + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return string + */ + private function _proceed() { + $step_name = $this->_getNextStep(); + + return $this->_runStepAction($step_name); + } + + /** + * Executes previous step + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return string + */ + private function _backward() { + $step_name = $this->_getPreviousStep(); + + return $this->_runStepAction($step_name); + } + + /** + * Executes step landing + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return string + */ + private function _landing() { + $step_name = $this->_getStepName(); + + return $this->_runStepAction($step_name); + } + + /** + * Executes step based on step class name + * + * @author KnowledgeTree Team + * @param string $step_name + * @access private + * @return string + */ + private function _runStepAction($stepName) { + $this->stepAction = new stepAction($stepName); + $this->stepAction->setUpStepAction($this->getSteps(), $this->getStepNames(), $this->getStepConfirmation(), $this->stepDisplayFirst(), $this->getSession(), $this->getProperties()); + + return $this->stepAction->doAction(); + } + + private function stepDisplayFirst() { + if($this->action == 'edit') + return false; // + $class = $this->stepAction->createStep(); // Get step class + return $class->displayFirst(); // Check if class needs to display first + } + + /** + * Set steps class names in string format + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return array + */ + private function _getOrders() { + return $this->orders; + } + + /** + * Set steps as names + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _xmlStepsToArray() { + if(isset($this->simpleXmlObj)) { + foreach($this->simpleXmlObj->steps->step as $d_step) { + $step_name = (string) $d_step[0]; + $this->stepClassNames[] = $step_name; + } + $this->_loadToSession('stepClassNames', $this->stepClassNames); + } + } + + /** + * Set steps as human readable strings + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _xmlStepsNames() { + if(isset($this->simpleXmlObj)) { + foreach($this->simpleXmlObj->steps->step as $d_step) { + $step_name = (string) $d_step[0]; + $this->stepNames[$step_name] = (string) $d_step['name']; + } + $this->_loadToSession('stepNames', $this->stepNames); + } + } + + /** + * Set steps order + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _xmlStepsOrders() { + if(isset($this->simpleXmlObj)) { + foreach($this->simpleXmlObj->steps->step as $d_step) { + if(isset($d_step['order'])) { + $step_name = (string) $d_step[0]; + $order = (string) $d_step['order']; + $this->orders[$order] = $step_name; // Store step order + } + } + $this->_loadToSession('orders', $this->orders); + } + } + + /** + * Set properties + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _xmlProperties() { + if(isset($this->simpleXmlObj)) { + $this->properties['fl_version'] = (string) $this->simpleXmlObj['version']; + $this->properties['fl_type'] = (string) $this->simpleXmlObj['type']; + $this->_loadToSession('properties', $this->properties); + } + } + + /** + * Steps + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _runSteps() { + $steps = $this->_getOrders(); + for ($i=1; $i< count($steps)+1; $i++) { + $this->_helper($steps[$i]); + } + + $this->_complete(); + } + + /** + * Complete cleanup process + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _complete() { + touch("firstlogin"); + } + + /** + * Steps helper + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _helper($className) { + $stepAction = new stepAction($className); // Instantiate a step action + $class = $stepAction->createStep(); // Get step class + if($class) { // Check if class Exists + if($class->run()) { // Check if step needs to be run + $class->setDataFromSession($className); // Set Session Information + $class->setPostConfig(); // Set any posted variables + $class->runStep(); // Run step + } + } else { + $util = new firstloginUtil(); + $util->error("Class File Missing in Step Directory: $className"); + exit(); + } + } + + /** + * Reset all session information on welcome landing + * + * @author KnowledgeTree Team + * @param none + * @access private + * @return void + */ + private function _resetSessions() { + if($this->session) { + if($this->_firstStepPeriod()) { + foreach ($this->getSteps() as $class) { + $this->session->un_setClass($class); + } + foreach ($this->getStepNames() as $class) { + $this->session->un_setClass($class); + } + foreach ($this->_getOrders() as $class) { + $this->session->un_setClass($class); + } + } + } + } + + function _loadFromSessions() { + $this->stepClassNames = $this->session->get('stepClassNames'); + if(!$this->stepClassNames) { + $this->_xmlStepsToArray(); // String steps + } + $this->stepNames = $this->session->get('stepNames'); + if(!$this->stepNames) { + $this->_xmlStepsNames(); + } + $this->Orders = $this->session->get('Orders'); + if(!$this->orders) { + $this->_xmlStepsOrders(); + } + $this->properties = $this->session->get('properties'); + if(!$this->properties) { + $this->_xmlProperties(); + } + } + + private function loadNeeded() { + $this->_readXml(); // Xml steps + $this->_resetSessions(); // Make sure session is cleared + $this->_loadFromSessions(); + if(isset($_POST['Next'])) { + $this->action = 'next'; + $this->response = 'next'; + } elseif (isset($_POST['Previous'])) { + $this->action = 'previous'; + $this->response = 'previous'; + } elseif (isset($_POST['Confirm'])) { + $this->action = 'confirm'; + $this->response = 'next'; + } elseif (isset($_POST['Edit'])) { + $this->action = 'edit'; + $this->response = 'next'; + } elseif (isset($_POST['Skip'])) { + $this->action = 'next'; + $this->response = 'next'; + } else { + $this->response = ''; + $this->action = ''; + } + $this->loadAjax(); + } + + private function loadAjax() { + if(isset($_GET['Next'])) { + $this->action = 'next'; + $this->response = 'next'; + } elseif (isset($_GET['Previous'])) { + $this->action = 'previous'; + $this->response = 'previous'; + } elseif (isset($_GET['Confirm'])) { + $this->action = 'confirm'; + $this->response = 'next'; + } elseif (isset($_GET['Edit'])) { + $this->action = 'edit'; + $this->response = 'next'; + } elseif (isset($_GET['Skip'])) { + $this->action = 'next'; + $this->response = 'next'; + } else { + $this->response = ''; + $this->action = ''; + } + } + + /** + * Main control to handle the flow + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return void + */ + public function step() { + $this->loadNeeded(); + switch($this->response) { + case 'next': + $step_name = $this->_getStepName(); + $res = $this->_runStepAction($step_name); + if($res == 'next') { + $this->_proceed(); // Load next window + } elseif ($res == 'confirm') { + if(!$this->stepDisplayFirst()) + $this->stepConfirmation = true; + $this->_landing(); + } elseif ($res == 'landing') { + $this->_landing(); + } else { + } + break; + case 'previous': + $this->_backward(); // Load previous page + break; + case 'install': + $util = new firstloginUtil(); + $util->redirect('../wizard/index.php?step_name=installtype'); + break; + default: + $this->_landing(); + break; + } + $this->stepAction->paintAction(); // Display step + } + + /** + * Returns the step number + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return integer $pos + */ + public function getStepPosition() { + $pos = 0; + foreach($this->simpleXmlObj->steps->step as $d_step) { + $step = (string) $d_step; + if ($step == $_GET['step_name']) { + break; + } + $pos++; + } + if(isset($_GET['step'])) { + if($_GET['step'] == "next") + $pos = $pos+1; + else + $pos = $pos-1; + } + + return $pos; + } + + /** + * Returns the step names for classes + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return array + */ + public function getSteps() { + return $this->stepClassNames; + } + + /** + * Returns the steps as human readable string + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return array + */ + public function getStepNames() { + return $this->stepNames; + } + + /** + * Returns whether or not a confirmation step is needed + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return boolean + */ + public function getStepConfirmation() { + return $this->stepConfirmation; + } + + /** + * Return properties + * + * @author KnowledgeTree Team + * @param string + * @access public + * @return string + */ + public function getProperties() { + return $this->properties; + } + + /** + * Returns session + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return boolean + */ + public function getSession() { + return $this->session; + } + + /** + * Dump of SESSION + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return array + */ + public function showSession() { + echo '
';
+        print_r($_SESSION);
+        echo '
'; + } + + /** + * Display errors that are not allowing the operation + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return void + */ + public function resolveErrors($errors) { + echo $errors; + exit(); + } + + private function _loadToSession($type, $values) { + if($values) { + $this->session->set($type , $values); + } + } +} + +?> diff --git a/setup/firstlogin/firstloginUtil.php b/setup/firstlogin/firstloginUtil.php new file mode 100644 index 0000000..8b30ac2 --- /dev/null +++ b/setup/firstlogin/firstloginUtil.php @@ -0,0 +1,107 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package First Login +* @version Version 0.1 +*/ +require_once("../wizard/installUtil.php"); + +class firstloginUtil extends InstallUtil { + /** + * Check system + * + * @author KnowledgeTree Team + * @access public + * @param none + * @return boolean + */ + public function isFirstLogin() { + if (file_exists(SYSTEM_DIR.'var'.DS.'bin'.DS."firstlogin.lock")) { + return true; + } + return false; + } + + public function error($error) { + $template_vars['fl_type'] = strtoupper(substr(INSTALL_TYPE,0,1)).substr(INSTALL_TYPE,1); + $template_vars['fl_version'] = $this->readVersion(); + $template_vars['error'] = $error; + $file = "templates/error.tpl"; + if (file_exists($file)) { + extract($template_vars); // Extract the vars to local namespace + ob_start(); + include($file); + $contents = ob_get_contents(); + ob_end_clean(); + echo $contents; + } + return false; + } + + /** + * Check if system needs + * + * @author KnowledgeTree Team + * @access public + * @param none + * @return mixed + */ + public function checkStructurePermissions() { + if(!$this->_checkPermission(WIZARD_DIR)) { // Check if Wizard Directory is writable + return 'firstlogin'; + } + + return true; + } + + /** + * Deletes first login lock file + * + * @author KnowledgeTree Team + * @access public + * @return void + */ + public function deleteFirstLogin() { + if(file_exists(SYSTEM_DIR.'var'.DS.'bin'.DS."firstlogin.lock")) + unlink(SYSTEM_DIR.'var'.DS.'bin'.DS."firstlogin.lock"); + } +} +?> \ No newline at end of file diff --git a/setup/wizard/session.php b/setup/firstlogin/firstloginWizard.php index cdaa443..a170336 100644 --- a/setup/wizard/session.php +++ b/setup/firstlogin/firstloginWizard.php @@ -1,10 +1,11 @@ startSession(); - } - - public function setSalt($salt) { - $this->salt = $salt; - } - + public function __construct(){} + /** - * Starts a session if one does not exist + * Check if system * * @author KnowledgeTree Team + * @access private * @param none - * @access public - * @return void - */ - public function startSession() { - if(!isset($_SESSION[$this->salt]['ready'])) { - @session_start(); - $_SESSION[$this->salt] ['ready'] = TRUE; - } + * @return boolean + */ + private function isFirstLogin() { + return $this->util->isFirstLogin(); } /** - * Sets a value key pair in session + * Display the wizard * * @author KnowledgeTree Team - * @param string $fld - * @param string $val - * @access public + * @access private + * @param string * @return void - */ - public function set($fld, $val) { - $this->startSession(); - $_SESSION[$this->salt] [$fld] = $val; - } - - /** - * Sets a value key pair in a class in session - * - * @author KnowledgeTree Team - * @param string $class - * @param string $fld - * @param string $val - * @access public - * @return void - */ - public function setClass($class , $k, $v) { - $this->startSession(); - $classArray = $this->get($class); - if(isset($classArray[$k])) { - $classArray[$k] = $v; - } else { - $classArray[$k] = $v; - } - $_SESSION[$this->salt] [ $class] = $classArray; - } - - /** - * Sets a error value key pair in a class in session - * - * @author KnowledgeTree Team - * @param string $class - * @param string $fld - * @param string $val - * @access public - * @return void - */ - public function setClassError($class, $k, $v) { - $this->startSession(); - $classArray = $this->get($class); - if(isset($classArray[$k])) { - $classArray[$k] = $v; + */ + public function display($response = null) { + if($response) { + $ins = new firstlogin(); // Instantiate + $ins->resolveErrors($response); // Run step } else { - $classArray[$k] = $v; + $ins = new firstlogin(new wSession()); // Instantiate and pass the session class + $ins->step(); // Run step } - $_SESSION[$this->salt] [ $class] = $classArray; - } - - /** - * Clear error values in a class session - * - * @author KnowledgeTree Team - * @param string $class - * @param string $fld - * @param string $val - * @access public - * @return void - */ - public function clearErrors($class) { - $classArray = $this->get($class); - unset($classArray['errors']); - $_SESSION[$this->salt] [ $class] = $classArray; } - + /** - * Unset a value in session + * Create file * * @author KnowledgeTree Team - * @param string $fld - * @access public + * @access private + * @param none * @return void - */ - public function un_set($fld) { - $this->startSession(); - unset($_SESSION[$this->salt] [$fld]); + */ + private function createFile() { + touch(SYSTEM_DIR.'var'.DS.'bin'.DS."firstlogin.lock"); } - + /** - * Unset a class value in session + * Remove file * * @author KnowledgeTree Team - * @param string $class - * @access public + * @access private + * @param none * @return void - */ - public function un_setClass($class) { - $this->startSession(); - if(isset($_SESSION[$this->salt] [$class])) - unset($_SESSION[$this->salt] [$class]); + */ + private function removeFile() { + unlink(SYSTEM_DIR.'var'.DS.'bin'.DS."firstlogin.lock"); } - + /** - * Destroy the session + * Load default values * * @author KnowledgeTree Team + * @access private * @param none - * @access public * @return void - */ - public function destroy() { - $this->startSession(); - unset($_SESSION[$this->salt]); - session_destroy(); - } - - /** - * Get a session value - * - * @author KnowledgeTree Team - * @param string $fld - * @access public - * @return string - */ - public function get($fld) { - $this->startSession(); - if(isset($_SESSION[$this->salt] [$fld])) - return $_SESSION[$this->salt] [$fld]; - return false; + */ + function load() { + if(isset($_GET['bypass'])) { + $this->setBypass($_GET['bypass']); + } + $this->setIUtil(new firstloginUtil()); } - + /** - * Check if a field exists in session + * Run pre system checks * * @author KnowledgeTree Team - * @param string $fld * @access public - * @return string - */ - public function is_set($fld) { - $this->startSession(); - return isset($_SESSION[$this->salt] [$fld]); + * @param none + * @return mixed + */ + public function systemChecks() { + $res = $this->util->checkStructurePermissions(); + if($res === true) return $res; + switch ($res) { + case "wizard": + $this->util->error("firstlogin directory is not writable (KT_Installation_Directory/setup/firstlogin/)"); + return 'firstlogin directory is not writable (KT_Installation_Directory/setup/firstlogin/)'; + break; + case "/": + $this->util->error("System root is not writable (KT_Installation_Directory/)"); + return "System root is not writable (KT_Installation_Directory/)"; + break; + default: + return true; + break; + } } - + /** - * Return a class from session + * Control all requests to wizard * * @author KnowledgeTree Team - * @param string $fld * @access public - * @return string - */ - public function getClass($class) { - return $_SESSION[$this->salt][$class]; + * @param none + * @return void + */ + public function dispatch() { + $this->load(); + if($this->getBypass() === "1") { + $this->removeFile(); + } elseif ($this->getBypass() === "0") { + $this->createFile(); + } + if($this->isFirstLogin()) { // Check if the systems + $response = $this->systemChecks(); + if($response === true) { + $this->display(); + } else { + exit(); + } + } else { + $this->util->error("System preferences run before. Finish"); + } } - } -if(isset($_GET['action'])) { - $func = $_GET['action']; - if($func != '') { - $ses = new Session(); - $method = "$func"; - $ses->$method(); - } -} +$ic = new firstloginWizard(); +$ic->dispatch(); ?> \ No newline at end of file diff --git a/setup/firstlogin/index.php b/setup/firstlogin/index.php new file mode 100644 index 0000000..73737ae --- /dev/null +++ b/setup/firstlogin/index.php @@ -0,0 +1,48 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package First Login +* @version Version 0.1 +*/ +$_GET['type'] = 'firstlogin'; +require_once("firstloginWizard.php"); +?> \ No newline at end of file diff --git a/setup/firstlogin/step.php b/setup/firstlogin/step.php new file mode 100644 index 0000000..827690e --- /dev/null +++ b/setup/firstlogin/step.php @@ -0,0 +1,167 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package +* @version Version 0.1 +*/ +class Step extends StepBase +{ + /** + * Flag if step needs + * + * @author KnowledgeTree Team + * @access public + * @var array + */ + private $run = false; + + public function __construct() { + $this->util = new firstloginUtil(); + $this->salt = 'firstlogin'; + } + + /** + * Checks if Confirm button has been clicked + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return boolean + */ + function firstlogin() { + if(isset($_POST['firstlogin'])) { + return true; + } + + return false; + } + + /** + * Checks if Confirm button has been clicked + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return boolean + */ + function installer() { + if(isset($_POST['Install'])) { + return true; + } + + return false; + } + + /** + * Load session data to post + * + * @author KnowledgeTree Team + * @params none + * @access private + * @return boolean + */ + public function setDataFromSession($class) { + if(empty($_SESSION[$this->salt][$class])) { + return false; + } + $_POST = isset($_SESSION[$this->salt]['firstlogin'][$class]) ? $_SESSION[$this->salt]['firstlogin'][$class]: ''; + + return true; + } + + /** + * Get session data from post + * + * @author KnowledgeTree Team + * @params none + * @access private + * @return boolean + */ + public function getDataFromSession($class) { + if(empty($_SESSION[$this->salt][$class])) { + return false; + } + + return $_SESSION[$this->salt][$class]; + } + + /** + * Runs step if required + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return void + */ + public function runStep() { + return ''; + } + + /** + * Return whether or not to a step + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return boolean + */ + public function run() { + return $this->run; + } + + /** + * Checks if next button has been clicked + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return boolean + */ + public function next() { + if(isset($_GET['Next'])) { + return true; + } + + return false; + } +} + +?> \ No newline at end of file diff --git a/setup/firstlogin/stepAction.php b/setup/firstlogin/stepAction.php new file mode 100644 index 0000000..e14da67 --- /dev/null +++ b/setup/firstlogin/stepAction.php @@ -0,0 +1,98 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package First Login +* @version Version 0.1 +*/ + +class stepAction extends stepActionBase { + /** + * Constructs step action object + * + * @author KnowledgeTree Team + * @access public + * @param string class name of the current step + */ + public function __construct($step) { + $this->stepName = $step; + } + + /** + * Instantiate a step. + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return object Step + */ + public function createStep() { + $step_class = "firstlogin".$this->makeCamelCase($this->stepName); + + return new $step_class(); + } + + /** + * Returns step tenplate content + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return string + */ + + public function getVars() { + $left = $this->getLeftMenu(); + $vars['left'] = $left; // Set left menu + $vars['fl_version'] = $this->properties['fl_version']; // Set version + $vars['fl_type'] = $this->properties['fl_type']; // Set type + if (KTPluginUtil::pluginIsActive('fs.FolderTemplatesPlugin.plugin')) { // Check if folder templates plugin is active + $oRegistry =& KTPluginRegistry::getSingleton(); + $oPlugin =& $oRegistry->getPlugin('fs.FolderTemplatesPlugin.plugin'); // Get a handle on the plugin + $ft_dir = $oPlugin->getDirs(); + } + $vars['ft_handle'] = $ft_dir; // Set type + return $vars; + } + +} + +?> \ No newline at end of file diff --git a/setup/firstlogin/steps/firstloginComplete.php b/setup/firstlogin/steps/firstloginComplete.php new file mode 100644 index 0000000..86489ce --- /dev/null +++ b/setup/firstlogin/steps/firstloginComplete.php @@ -0,0 +1,98 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package First Login +* @version Version 0.1 +*/ + +class firstloginComplete extends Step { + /** + * Flag if step needs to run silently + * + * @access protected + * @var boolean + */ + protected $silent = true; + + /** + * Returns step state + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return string + */ + function doStep() { + $this->temp_variables = array( + "step_name"=>"complete", + "silent"=>$this->silent); + if(!$this->inStep("complete")) { + return $this->doRun(); + } + if($this->next()) { // Next click + $this->completeWizard(); // Apply folder template structures + return 'login'; // And go to next step + } + + return 'landing'; + } + + function doRun() { + $ft_dir = ""; + if (KTPluginUtil::pluginIsActive('fs.FolderTemplatesPlugin.plugin')) { // Check if folder templates plugin is active + $oRegistry =& KTPluginRegistry::getSingleton(); + $oPlugin =& $oRegistry->getPlugin('fs.FolderTemplatesPlugin.plugin'); // Get a handle on the plugin + $ft_dir = $oPlugin->getDirs(); + } + $this->temp_variables['ft_dir'] = $ft_dir; + + return 'landing'; + } + + function completeWizard() { + $this->util->deleteFirstLogin(); + } + + public function getErrors() { + return $this->error; + } +} +?> \ No newline at end of file diff --git a/setup/firstlogin/steps/firstloginTemplates.php b/setup/firstlogin/steps/firstloginTemplates.php new file mode 100644 index 0000000..28b23e6 --- /dev/null +++ b/setup/firstlogin/steps/firstloginTemplates.php @@ -0,0 +1,147 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package First Login +* @version Version 0.1 +*/ + +// Load needed system files. +require_once(SYSTEM_DIR . "config/dmsDefaults.php"); +require_once(KT_LIB_DIR . '/plugins/plugin.inc.php'); +require_once(KT_LIB_DIR . '/plugins/pluginregistry.inc.php'); + +class firstloginTemplates extends Step { + /** + * Flag if step needs to run silently + * + * @access protected + * @var boolean + */ + protected $silent = true; + + /** + * Returns step state + * + * @author KnowledgeTree Team + * @param none + * @access public + * @return string + */ + function doStep() { + $this->temp_variables = array( + "step_name"=>"templates", + "silent"=>$this->silent, + ); + if(!$this->inStep("templates")) { // Landing + $this->doRun(); // Set folder structure templates + return 'landing'; + } + if($this->next()) { // Next click + $this->applyTemplates(); // Apply folder template structures + return 'next'; // And go to next step + } else if($this->skip()) { // Skip Step + return 'next'; + } + + $this->doRun(); // Set folder structure templates + return 'landing'; // Default to landing + } + + function doRun() { + $ft_dir = ""; + if (KTPluginUtil::pluginIsActive('fs.FolderTemplatesPlugin.plugin')) { // Check if folder templates plugin is active + $oRegistry =& KTPluginRegistry::getSingleton(); + $oPlugin =& $oRegistry->getPlugin('fs.FolderTemplatesPlugin.plugin'); // Get a handle on the plugin + $ft_dir = $oPlugin->getDirs(); + } + $this->temp_variables['aFolderTemplates'] = $this->getTemplates(); + $this->temp_variables['ft_dir'] = $ft_dir; + return 'landing'; + } + + function applyTemplates() { + $templateId = KTUtil::arrayGet($_POST['data'], "templateId", 0); + if($templateId < 1) { + $templateId = KTUtil::arrayGet($_GET, "templateId", 0);// Could be ajax call + } + if($templateId > 0) { + if (KTPluginUtil::pluginIsActive('fs.FolderTemplatesPlugin.plugin')) { // Check if folder templates plugin is active + $oRegistry =& KTPluginRegistry::getSingleton(); + $oPlugin =& $oRegistry->getPlugin('fs.FolderTemplatesPlugin.plugin'); // Get a handle on the plugin + return $oPlugin->firstLoginAction(1, $templateId); + } + } + return false; + } + + function getTemplates() { + if (KTPluginUtil::pluginIsActive('fs.FolderTemplatesPlugin.plugin')) { // Check if folder templates plugin is active + $oRegistry =& KTPluginRegistry::getSingleton(); + $oPlugin =& $oRegistry->getPlugin('fs.FolderTemplatesPlugin.plugin'); // Get a handle on the plugin + return $oPlugin->getFirstLoginTemplates(); + } + } + + function getTemplateNodes() { + if (KTPluginUtil::pluginIsActive('fs.FolderTemplatesPlugin.plugin')) { // Check if folder templates plugin is active + $oRegistry =& KTPluginRegistry::getSingleton(); + $oPlugin =& $oRegistry->getPlugin('fs.FolderTemplatesPlugin.plugin'); // Get a handle on the plugin + return $oPlugin->getFirstLoginTemplates(); + } + } + + public function getErrors() { + return $this->error; + } + + /** + * Stores varibles used by template + * + * @author KnowledgeTree Team + * @params none + * @access public + * @return array + */ + public function getStepVars() { + return $this->temp_variables; + } +} +?> \ No newline at end of file diff --git a/setup/firstlogin/templates/complete.tpl b/setup/firstlogin/templates/complete.tpl new file mode 100644 index 0000000..553de77 --- /dev/null +++ b/setup/firstlogin/templates/complete.tpl @@ -0,0 +1,16 @@ +
+

Preferences Completed

+
+

Your system preferences have been applied

+
+ +
+ +js('form.js'); } ?> \ No newline at end of file diff --git a/setup/firstlogin/templates/error.tpl b/setup/firstlogin/templates/error.tpl new file mode 100644 index 0000000..bf75326 --- /dev/null +++ b/setup/firstlogin/templates/error.tpl @@ -0,0 +1,67 @@ + + + + + KnowledgeTree Installer + + + + + + +
+ +
+
+ + +
+
+
+
+

Welcome to the KnowledgeTree Migration Wizard

+ ".$error.""; + ?> + + '; + foreach ($errors as $msg){ + echo $msg . "
"; + ?> + + '; + } + } + ?> +
+
+
+
+
+
 
+
+ + +
+ + + \ No newline at end of file diff --git a/setup/firstlogin/templates/errors.tpl b/setup/firstlogin/templates/errors.tpl new file mode 100644 index 0000000..f48f1df --- /dev/null +++ b/setup/firstlogin/templates/errors.tpl @@ -0,0 +1,14 @@ +

Welcome to the KnowledgeTree Migration Wizard

+ +
+ +'; + foreach ($errors as $msg){ + echo $msg . "
\n"; + } + echo '
'; +} +?> + \ No newline at end of file diff --git a/setup/firstlogin/templates/templates.tpl b/setup/firstlogin/templates/templates.tpl new file mode 100644 index 0000000..8c57881 --- /dev/null +++ b/setup/firstlogin/templates/templates.tpl @@ -0,0 +1,60 @@ +
+

Apply Templates

+

You can select a base folder layout to apply to your root folder of KnowledgeTree.

+
+
+

Choose a template, if you would like to generate predefined folders.

+

+ +
+ + + Folder Template Layout : +
+
+
+ + + + + + + + + + +
+
+
+ + +
+ +js('form.js'); } ?> \ No newline at end of file diff --git a/setup/firstlogin/templates/wizard.tpl b/setup/firstlogin/templates/wizard.tpl new file mode 100644 index 0000000..826faef --- /dev/null +++ b/setup/firstlogin/templates/wizard.tpl @@ -0,0 +1,52 @@ + + + + + KnowledgeTree Installer + tpjs('jquery-1.3.2.js'); ?> + js('jquery-1.4.2.min.js'); ?> + tpjs('jquery_noconflict.js'); ?> + js('firstlogin.js'); ?> + css('wizard.css'); ?> + css('firstlogin.css'); ?> + css('ie6.css'); ?> + css('ie7.css'); ?> + css('ie8.css'); ?> + css('community.css'); ?> + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
 
+
+ + +
+
+ + + \ No newline at end of file diff --git a/setup/firstlogin/wSession.php b/setup/firstlogin/wSession.php new file mode 100644 index 0000000..fcd3d55 --- /dev/null +++ b/setup/firstlogin/wSession.php @@ -0,0 +1,62 @@ +. +* +* You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, +* California 94120-7775, or email info@knowledgetree.com. +* +* The interactive user interfaces in modified source and object code versions +* of this program must display Appropriate Legal Notices, as required under +* Section 5 of the GNU General Public License version 3. +* +* In accordance with Section 7(b) of the GNU General Public License version 3, +* these Appropriate Legal Notices must retain the display of the "Powered by +* KnowledgeTree" logo and retain the original copyright notice. If the display of the +* logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices +* must display the words "Powered by KnowledgeTree" and retain the original +* copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** +* +* @copyright 2008-2010, KnowledgeTree Inc. +* @license GNU General Public License version 3 +* @author KnowledgeTree Team +* @package First Login +* @version Version 0.1 +*/ +class wSession extends SessionBase +{ + public $salt = 'firstlogin'; + + /** + * Constructs session object + * + * @author KnowledgeTree Team + * @access public + * @param none + */ + public function __construct() { + $this->setSalt($this->salt); + $this->startSession(); + } + +} +?> \ No newline at end of file diff --git a/setup/migrate/index.php b/setup/migrate/index.php index 5486fc7..d05182e 100644 --- a/setup/migrate/index.php +++ b/setup/migrate/index.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/migrateUtil.php b/setup/migrate/migrateUtil.php index f13ab5d..14f174b 100644 --- a/setup/migrate/migrateUtil.php +++ b/setup/migrate/migrateUtil.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/migrateWizard.php b/setup/migrate/migrateWizard.php index 82cdeaf..14ead3f 100644 --- a/setup/migrate/migrateWizard.php +++ b/setup/migrate/migrateWizard.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,56 +32,21 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater * @version Version 0.1 */ -include("../wizard/path.php"); // Paths -/** - * Auto loader to bind migrater package - * - * @param string $class - * @return void - */ -function __autoload($class) { // Attempt and autoload classes - $class = strtolower(substr($class,0,1)).substr($class,1); // Linux Systems. - if ($class == "template") { // Load existing templating classes - require_once("../wizard/template.php"); - require_once("../wizard/lib/helpers/htmlHelper.php"); - } else { - if(file_exists(WIZARD_DIR."$class.php")) { - require_once(WIZARD_DIR."$class.php"); - } elseif (file_exists(STEP_DIR."$class.php")) { - require_once(STEP_DIR."$class.php"); - } elseif (file_exists(WIZARD_LIB."$class.php")) { - require_once(WIZARD_LIB."$class.php"); - } - } -} - -class MigrateWizard { - /** - * Migrate bypass flag - * - * @author KnowledgeTree Team - * @access protected - * @var mixed - */ - protected $bypass = null; - - /** - * Reference to migrater utility object - * - * @author KnowledgeTree Team - * @access protected - * @var boolean - */ - protected $util = null; +require_once("../wizard/share/wizardBase.php"); +class MigrateWizard extends WizardBase { /** * Constructs migrateation wizard object * @@ -110,65 +75,17 @@ class MigrateWizard { * @param string * @return void */ - public function displayMigrater($response = null) { + public function display($response = null) { if($response) { $ins = new Migrater(); // Instantiate the migrater $ins->resolveErrors($response); // Run step } else { - $ins = new Migrater(new Session()); // Instantiate the migrater and pass the session class + $ins = new Migrater(new wSession()); // Instantiate the migrater and pass the session class $ins->step(); // Run step } } /** - * Set bypass flag - * - * @author KnowledgeTree Team - * @access private - * @param boolean - * @return void - */ - private function setBypass($bypass) { - $this->bypass = $bypass; - } - - /** - * Set util reference - * - * @author KnowledgeTree Team - * @access private - * @param object migrater utility - * @return void - */ - private function setIUtil($util) { - $this->util = $util; - } - - /** - * Get bypass flag - * - * @author KnowledgeTree Team - * @access public - * @param none - * @return boolean - */ - public function getBypass() { - return $this->bypass; - } - - /** - * Bypass and force an migrate - * - * @author KnowledgeTree Team - * @access private - * @param none - * @return boolean - */ - private function bypass() { - - } - - /** * Create migrate file * * @author KnowledgeTree Team @@ -177,7 +94,7 @@ class MigrateWizard { * @return void */ private function createMigrateFile() { - touch("migrate"); + touch(SYSTEM_DIR.'var'.DS.'bin'.DS."migrate.lock"); } /** @@ -189,7 +106,7 @@ class MigrateWizard { * @return void */ private function removeMigrateFile() { - unlink("migrate"); + unlink(SYSTEM_DIR.'var'.DS.'bin'.DS."migrate.lock"); } /** @@ -251,7 +168,7 @@ class MigrateWizard { if(!$this->isSystemMigrated()) { // Check if the systems not migrated $response = $this->systemChecks(); if($response === true) { - $this->displayMigrater(); + $this->display(); } else { exit(); } diff --git a/setup/migrate/migrater.php b/setup/migrate/migrater.php index d7b6907..ece441c 100644 --- a/setup/migrate/migrater.php +++ b/setup/migrate/migrater.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater @@ -553,12 +557,7 @@ class Migrater { $util = new MigrateUtil(); $util->redirect('../wizard/index.php?step_name=installtype'); break; -// case 'binstall': -// $util = new MigrateUtil(); -// $util->redirect('../wizard/index.php?step_name=dependencies'); -// break; default: - // TODO : handle silent $this->_landing(); break; } diff --git a/setup/migrate/step.php b/setup/migrate/step.php index 2457e49..ef01406 100644 --- a/setup/migrate/step.php +++ b/setup/migrate/step.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,238 +31,34 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater * @version Version 0.1 */ -class Step +class Step extends StepBase { /** - * List of variables to be loaded to template - * - * @author KnowledgeTree Team - * @access protected - * @var array - */ - protected $temp_variables = array(); - - /** - * List of errors encountered by step - * - * @author KnowledgeTree Team - * @access protected - * @var array - */ - protected $error = array(); - - /** - * List of warnings encountered by step - * - * @author KnowledgeTree Team - * @access protected - * @var array - */ - protected $warnings = array(); - - /** - * Flag to store class information in session - * - * @author KnowledgeTree Team - * @access public - * @var array - */ - protected $storeInSession = false; - - /** * Flag if step needs to be migrated * * @author KnowledgeTree Team * @access public * @var array */ - protected $runMigrate = false; - - /** - * Step order - * - * @author KnowledgeTree Team - * @access public - * @var string - */ - protected $order = false; - - /** - * Flag if step needs to run silently - * - * @author KnowledgeTree Team - * @access public - * @var boolean - */ - protected $silent = false; - - public $displayFirst = false; - - private $salt = 'migrate'; - - /** - * Reference to utility object - * - * @author KnowledgeTree Team - * @access protected - * @var object - */ - public $util; + private $runMigrate = false; public function __construct() { $this->util = new MigrateUtil(); + $this->salt = 'migrate'; } /** - * Returns step state - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return string - */ - public function doStep() - { - return ''; - } - - public function displayFirst() { - return $this->displayFirst; - } - - /** - * Returns step variables - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return array - */ - public function getStepVars() - { - return $this->temp_variables; - } - - /** - * Returns step errors - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return array - */ - public function getErrors() { - return $this->error; - } - - /** - * Returns step errors - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return array - */ - public function getWarnings() { - return $this->warnings; - } - - /** - * Load default step values - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return void - */ - public function loadDefaults() { - - } - - /** - * Return default step values - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return array - */ - public function getDefaults() { - return array(); - } - - /** - * Checks if edit button has been clicked - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function edit() { - if(isset($_POST['Edit'])) { - return true; - } - - return false; - } - - /** - * Checks if next button has been clicked - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function next() { - if(isset($_POST['Next'])) { - return true; - } - - return false; - } - - /** - * Checks if previous button has been clicked - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function previous() { - if(isset($_POST['Previous'])) { - return true; - } - - return false; - } - - /** - * Checks if Confirm button has been clicked - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - function confirm() { - if(isset($_POST['Confirm'])) { - return true; - } - - return false; - } - - /** * Checks if Confirm button has been clicked * * @author KnowledgeTree Team @@ -293,20 +89,6 @@ class Step return false; } - /** - * Checks if we are currently in this class step - * - * @author KnowledgeTree Team - * @param string - * @access public - * @return boolean - */ - public function inStep($name) { - if(!isset($_GET['step_name'])) return false; - if($_GET['step_name'] == $name) - return true; - return false; - } /** * Load session data to post @@ -342,30 +124,6 @@ class Step } /** - * Safer way to return post data - * - * @author KnowledgeTree Team - * @params SimpleXmlObject $simplexml - * @access public - * @return void - */ - public function getPostSafe($key) { - return isset($_POST[$key]) ? $_POST[$key] : ""; - } - - /** - * Safer way to return post data - * - * @author KnowledgeTree Team - * @params SimpleXmlObject $simplexml - * @access public - * @return void - */ - public function getPostBoolean($key) { - return isset($_POST[$key]) ? $_POST[$key] : false; - } - - /** * Runs step migrate if required * * @author KnowledgeTree Team @@ -378,18 +136,6 @@ class Step } /** - * Return whether or not to store a step information in session - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function storeInSession() { - return $this->storeInSession; - } - - /** * Return whether or not to a step has to be migrated * * @author KnowledgeTree Team @@ -399,49 +145,7 @@ class Step */ public function runMigrate() { return $this->runMigrate; - } - - public function setPostConfig() { - return ''; - } - - /** - * Return whether or not to a step has to be in silent mode - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function silentMode() { - return $this->silent; - } - - /** - * Set step errors - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return array - */ - public function setErrors($error) { - $this->error = $error; - } - - /** - * Is the installation - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return string - */ - public function isCe() { - if($this->util->getVersionType() == "community") - return true; - return false; - } + } } ?> \ No newline at end of file diff --git a/setup/migrate/stepAction.php b/setup/migrate/stepAction.php index bf99bdd..bc4ba5e 100644 --- a/setup/migrate/stepAction.php +++ b/setup/migrate/stepAction.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,78 +32,19 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater * @version Version 0.1 */ -class stepAction { - /** - * Step class name - * - * @author KnowledgeTree Team - * @access protected - * @var string - */ - protected $stepName = ''; - - /** - * Step names for classes - * - * @author KnowledgeTree Team - * @access protected - * @var array - */ - protected $stepClassNames = array(); - - /** - * Flag if step needs confirmation - * - * @author KnowledgeTree Team - * @access protected - * @var boolean - */ - protected $displayConfirm = false; - - /** - * Returns whether or not to display the confirmation page first - * - * @author KnowledgeTree Team - * @access protected - * @var boolean - */ - protected $displayFirst = false; - - /** - * List of migrate properties - * - * @author KnowledgeTree Team - * @access protected - * @var boolean - */ - protected $migrateProperties = array(); - - /** - * Reference to session object - * - * @author KnowledgeTree Team - * @access protected - * @var object Session - */ - protected $session = null; - - /** - * Reference to current step object - * - * @author KnowledgeTree Team - * @access protected - * @var object class Step - */ - protected $action = null; - +class stepAction extends stepActionBase { /** * Constructs step action object * @@ -116,129 +57,6 @@ class stepAction { } /** - * Helper to initialize step actions - * - * @author KnowledgeTree Team - * @param string - * @access public - * @return string - */ - public function setUpStepAction($steps, $stepNames, $stepConfirmation, $stepDisplayFirst, $session, $migrateProperties) { - $this->setSteps($steps); - $this->setStepNames($stepNames); - $this->setDisplayConfirm($stepConfirmation); - $this->setDisplayFirst($stepDisplayFirst); - $this->loadSession($session); - $this->setMigrateProperties($migrateProperties); - } - - /** - * Sets steps class names in string format - * - * @author KnowledgeTree Team - * @param array - * @access public - * @return void - */ - public function setSteps($stepClassNames) { - $this->stepClassNames = $stepClassNames; - } - - /** - * Sets steps in human readable string format - * - * @author KnowledgeTree Team - * @param array - * @access public - * @return void - */ - public function setStepNames($step_names) { - $this->step_names = $step_names; - } - - /** - * Sets confirmation page flag - * - * @author KnowledgeTree Team - * @param boolean - * @access public - * @return void - */ - public function setDisplayConfirm($displayConfirm) { - $this->displayConfirm = $displayConfirm; - } - - /** - * Sets confirmation page first flag - * - * @author KnowledgeTree Team - * @param boolean - * @access public - * @return void - */ - public function setDisplayFirst($displayFirst) { - $this->displayFirst = $displayFirst; - } - - /** - * Sets session object - * - * @author KnowledgeTree Team - * @param object Session - * @access public - * @return void - */ - public function loadSession($ses) { - $this->session = $ses; - } - - /** - * Sets migrate properties - * - * @author KnowledgeTree Team - * @param array - * @access public - * @return void - */ - public function setMigrateProperties($migrateProperties) { - $this->migrateProperties = $migrateProperties; - } - - /** - * Main control to handle the steps actions - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return string - */ - public function doAction() { - if($this->stepName != '') { - $this->action = $this->createStep(); - if(!$this->action) { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Class Files Missing in Step Directory'); - } - $response = $this->action->doStep(); - if($this->action->storeInSession()) { // Check if class values need to be stored in session - $this->_loadStepToSession($this->stepName); // Send class to session - } - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Class File Missing in Step Directory'); - - } - if ($response == 'error') { - $this->_handleErrors(); // Send Errors to session - } else { - $this->_clearErrors($this->stepName); // Send Errors to session - } - return $response; - } - - /** * Instantiate a step. * * @author KnowledgeTree Team @@ -253,127 +71,6 @@ class stepAction { } /** - * Converts string to camel case - * - * @author KnowledgeTree Team - * @param string - * @access public - * @return string - */ - public function makeCamelCase($str) { - $upper=ucwords($str); - $str=str_replace('_', '', $upper); - - return $str; - } - - /** - * Converts string to human readable heading - * - * @author KnowledgeTree Team - * @param string - * @access public - * @return string - */ - public function makeHeading($str) { - $str = str_replace('_', ' ', $str); - $str = ucwords($str); - - return $str; - } - - /** - * Returns current step name - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return string - */ - public function getCurrentStepName() { - if($this->stepName != 'errors') - return $this->step_names[$this->stepName]; - return ''; - } - - /** - * Returns left menu - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return string - */ - public function getLeftMenu() - { - $sideMenuElements = array(); - $active = false; - if($this->stepClassNames) { - $ele = array(); - foreach ($this->stepClassNames as $k=>$step) { - $ele['step'] = $step; - if($this->step_names[$step] != '') { - $ele['name'] = $this->step_names[$step]; - } else { - $ele['name'] = $this->makeHeading($step); - } - if($step == $this->stepName) { - $ele['class'] = 'current'; - $active = true; - } else { - if($active) { - $ele['class'] = 'inactive'; - } else { - $ele['class'] = 'indicator'; - } - } - $sideMenuElements[] = $ele; - } - } - $step_tpl = new Template("..".DS."wizard".DS."templates".DS."sidemenu.tpl"); // Create template - $step_tpl->set("sideMenuElements", $sideMenuElements); // Set side menu elements - $step_tpl->set("ajax", AJAX); // Set ajax state - - return $step_tpl->fetch(); - } - - /** - * Returns confirmation page flag - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function displayConfirm() { - return $this->displayConfirm; - } - - /** - * Returns whether or not to display the confirmation page first - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function displayFirst() { - return $this->displayFirst; - } - - /** - * Returns session object - * - * @author KnowledgeTree Team - * @param object Session - * @access public - * @return object - */ - public function getSession() { - return $this->session; - } - - /** * Returns step tenplate content * * @author KnowledgeTree Team @@ -381,154 +78,15 @@ class stepAction { * @access public * @return string */ - public function paintAction() { - $step_errors = $this->action->getErrors(); // Get errors - $step_warnings = $this->action->getWarnings(); // Get warnings - if($this->displayConfirm()) { // Check if theres a confirm step - $template = "templates/{$this->stepName}_confirm.tpl"; - } else { - if($this->displayFirst()) { - $template = "templates/{$this->stepName}_confirm.tpl"; - } else { - $template = "templates/{$this->stepName}.tpl"; - } - } - $step_tpl = new Template($template); - $step_tpl->set("errors", $step_errors); // Set template errors - $step_tpl->set("warnings", $step_warnings); // Set template warnings - $step_vars = $this->action->getStepVars(); // Get template variables - $step_tpl->set("step_vars", $step_vars); // Set template errors - $this->loadToSes($step_vars); - $this->loadToTpl($step_tpl, $step_vars); - // TODO: Force because it does not always recognize ajax request - if(AJAX && !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { - echo $step_tpl->fetch(); - } else { - $content = $step_tpl->fetch(); - $tpl = new Template("templates/wizard.tpl"); - $vars = $this->getVars(); // Get template variables - $tpl->set("vars", $vars); // Set template errors - $tpl->set('content', $content); - echo $tpl->fetch(); - } - } - - public function loadToSes($step_vars) { - if($this->action->storeInSession()) { // Check if class values need to be stored in session - foreach ($step_vars as $key => $value) { // Set template variables - $this->_loadValueToSession($this->stepName, $key, $value); - } - } - } - - public function loadToTpl($step_tpl, $step_vars) { - foreach ($step_vars as $key => $value) { // Set template variables - $step_tpl->set($key, $value); // Load values to session - } - } public function getVars() { $left = $this->getLeftMenu(); $vars['left'] = $left; // Set left menu - $vars['migrate_version'] = $this->migrateProperties['migrate_version']; // Set version - $vars['migrate_type'] = $this->migrateProperties['migrate_type']; // Set type + $vars['migrate_version'] = $this->properties['migrate_version']; // Set version + $vars['migrate_type'] = $this->properties['migrate_type']; // Set type return $vars; } - /** - * Load class to session - * - * @author KnowledgeTree Team - * @param string $class name of class - * @param array $v array of values - * @param boolean $overwrite whether or not to overwrite existing - * @access private - * @return void - */ - private function _loadStepToSession($class, $v = array(), $overwrite = false) { - if($this->session != null) { - if($overwrite) { - $this->session->set($class , $v); - } else { - if(!$this->session->is_set($class)) - $this->session->set($class , $v); - } - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Sessions Are Disabled'); - } - } - - /** - * Load class value to session - * - * @author KnowledgeTree Team - * @param string $class name of class - * @param string $k key value - * @param string $v value to store - * @param boolean $overwrite whether or not to overwrite existing - * @access private - * @return void - */ - private function _loadValueToSession($class, $k, $v) { - if($this->session != null) { - $this->session->setClass($class, $k, $v); - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Sessions Are Disabled'); - } - } - - /** - * Load all class errors value to session - * - * @author KnowledgeTree Team - * @param none - * @access private - * @return void - */ - private function _handleErrors() {// TODO: handle multiple errors - $step_errors = $this->action->getErrors(); // Get errors - foreach ($step_errors as $key => $value) { - $this->_loadErrorToSession($this->stepName, $key, $value); // Load values session - } - } - - /** - * Remove all class errors value to session - * - * @author KnowledgeTree Team - * @param none - * @access private - * @return void - */ - private function _clearErrors($class) { - if($this->session) { - $this->session->clearErrors($class); - } - } - /** - * Load class error value to session - * - * @author KnowledgeTree Team - * @param string $class name of class - * @param string $k key value - * @param string $v value to store - * @param boolean $overwrite whether or not to overwrite existing - * @access private - * @return void - */ - private function _loadErrorToSession($class, $k = "errors", $v) { - if($this->session != null) { - $this->session->setClassError($class, $k, $v); - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Sessions Are Disabled'); - } - } } ?> \ No newline at end of file diff --git a/setup/migrate/steps/migrateComplete.php b/setup/migrate/steps/migrateComplete.php index a3eaf6b..76f59a9 100644 --- a/setup/migrate/steps/migrateComplete.php +++ b/setup/migrate/steps/migrateComplete.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/steps/migrateDatabase.php b/setup/migrate/steps/migrateDatabase.php index 0a5f0a0..ab7ce34 100755 --- a/setup/migrate/steps/migrateDatabase.php +++ b/setup/migrate/steps/migrateDatabase.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/steps/migrateErrors.php b/setup/migrate/steps/migrateErrors.php index 8890d12..aaa306a 100644 --- a/setup/migrate/steps/migrateErrors.php +++ b/setup/migrate/steps/migrateErrors.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/steps/migrateInstallation.php b/setup/migrate/steps/migrateInstallation.php index 97a16fe..3811a53 100644 --- a/setup/migrate/steps/migrateInstallation.php +++ b/setup/migrate/steps/migrateInstallation.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/steps/migrateServices.php b/setup/migrate/steps/migrateServices.php index 852993f..7e5a8f1 100644 --- a/setup/migrate/steps/migrateServices.php +++ b/setup/migrate/steps/migrateServices.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Migrater diff --git a/setup/migrate/templates/error.tpl b/setup/migrate/templates/error.tpl index 7e68097..1f49699 100644 --- a/setup/migrate/templates/error.tpl +++ b/setup/migrate/templates/error.tpl @@ -3,7 +3,7 @@ KnowledgeTree Installer - + @@ -17,7 +17,7 @@ -
+
diff --git a/setup/migrate/templates/wizard.tpl b/setup/migrate/templates/wizard.tpl index 4133c7d..28493c2 100644 --- a/setup/migrate/templates/wizard.tpl +++ b/setup/migrate/templates/wizard.tpl @@ -3,7 +3,7 @@ KnowledgeTree Installer - js('jquery.js'); ?> + tpjs('jquery-1.3.2.js'); ?> js('jquery.form.js'); ?> js('jquery.blockUI.js'); ?> js('jquery.hotkeys.js'); ?> @@ -26,7 +26,7 @@
-
+
'; - return $menu; - } - - /** - * Returns confirmation page flag - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function displayConfirm() { - return $this->displayConfirm; - } - - /** - * Returns whether or not to display the confirmation page first - * - * @author KnowledgeTree Team - * @param none - * @access public - * @return boolean - */ - public function displayFirst() { - return $this->displayFirst; - } - - /** - * Returns session object - * - * @author KnowledgeTree Team - * @param object Session - * @access public - * @return object - */ - public function getSession() { - return $this->session; - } - - /** * Returns step tenplate content * * @author KnowledgeTree Team @@ -377,8 +78,7 @@ class stepAction { * @access public * @return string */ - public function paintAction() { - + public function paintAction() { $step_errors = $this->action->getErrors(); // Get errors $step_warnings = $this->action->getWarnings(); // Get warnings if($this->displayConfirm()) { // Check if theres a confirm step @@ -412,105 +112,12 @@ class stepAction { public function getVars() { $left = $this->getLeftMenu(); $vars['left'] = $left; // Set left menu - $vars['upgrade_version'] = $this->upgradeProperties['upgrade_version']; // Set version - $vars['upgrade_type'] = $this->upgradeProperties['upgrade_type']; // Set type + $vars['upgrade_version'] = $this->properties['upgrade_version']; // Set version + $vars['upgrade_type'] = $this->properties['upgrade_type']; // Set type return $vars; } - /** - * Load class to session - * - * @author KnowledgeTree Team - * @param string $class name of class - * @param array $v array of values - * @param boolean $overwrite whether or not to overwrite existing - * @access private - * @return void - */ - private function _loadStepToSession($class, $v = array(), $overwrite = false) { - if($this->session != null) { - if($overwrite) { - $this->session->set($class , $v); - } else { - if(!$this->session->is_set($class)) - $this->session->set($class , $v); - } - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Sessions Are Disabled'); - } - } - - /** - * Load class value to session - * - * @author KnowledgeTree Team - * @param string $class name of class - * @param string $k key value - * @param string $v value to store - * @param boolean $overwrite whether or not to overwrite existing - * @access private - * @return void - */ - private function _loadValueToSession($class, $k, $v) { - if($this->session != null) { - $this->session->setClass($class, $k, $v); - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Sessions Are Disabled'); - } - } - - /** - * Load all class errors value to session - * - * @author KnowledgeTree Team - * @param none - * @access private - * @return void - */ - private function _handleErrors() {// TODO: handle multiple errors - $step_errors = $this->action->getErrors(); // Get errors - foreach ($step_errors as $key => $value) { - $this->_loadErrorToSession($this->stepName, $key, $value); // Load values session - } - } - /** - * Remove all class errors value to session - * - * @author KnowledgeTree Team - * @param none - * @access private - * @return void - */ - private function _clearErrors($class) { - if($this->session) { - $this->session->clearErrors($class); - } - } - /** - * Load class error value to session - * - * @author KnowledgeTree Team - * @param string $class name of class - * @param string $k key value - * @param string $v value to store - * @param boolean $overwrite whether or not to overwrite existing - * @access private - * @return void - */ - private function _loadErrorToSession($class, $k = "errors", $v) { - if($this->session != null) { - $this->session->setClassError($class, $k, $v); - } else { - $this->stepName = 'errors'; - $this->action = $this->createStep(); - $this->action->error = array('Sessions Are Disabled'); - } - } } ?> \ No newline at end of file diff --git a/setup/upgrade/steps/upgradeBackup.php b/setup/upgrade/steps/upgradeBackup.php index d34d720..1d5ed05 100644 --- a/setup/upgrade/steps/upgradeBackup.php +++ b/setup/upgrade/steps/upgradeBackup.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrader diff --git a/setup/upgrade/steps/upgradeComplete.php b/setup/upgrade/steps/upgradeComplete.php index 8fa216c..707e2bb 100644 --- a/setup/upgrade/steps/upgradeComplete.php +++ b/setup/upgrade/steps/upgradeComplete.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrader diff --git a/setup/upgrade/steps/upgradeDatabase.php b/setup/upgrade/steps/upgradeDatabase.php index 29b5883..a371993 100644 --- a/setup/upgrade/steps/upgradeDatabase.php +++ b/setup/upgrade/steps/upgradeDatabase.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrader diff --git a/setup/upgrade/steps/upgradeErrors.php b/setup/upgrade/steps/upgradeErrors.php index cdf681e..81a4de3 100644 --- a/setup/upgrade/steps/upgradeErrors.php +++ b/setup/upgrade/steps/upgradeErrors.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrade diff --git a/setup/upgrade/steps/upgradeInstallation.php b/setup/upgrade/steps/upgradeInstallation.php index 417f834..c28a693 100644 --- a/setup/upgrade/steps/upgradeInstallation.php +++ b/setup/upgrade/steps/upgradeInstallation.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrade diff --git a/setup/upgrade/steps/upgradeRestore.php b/setup/upgrade/steps/upgradeRestore.php index fee78dc..4f57b45 100644 --- a/setup/upgrade/steps/upgradeRestore.php +++ b/setup/upgrade/steps/upgradeRestore.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrader diff --git a/setup/upgrade/steps/upgradeWelcome.php b/setup/upgrade/steps/upgradeWelcome.php index e05a52a..29f0c56 100644 --- a/setup/upgrade/steps/upgradeWelcome.php +++ b/setup/upgrade/steps/upgradeWelcome.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright(C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the @@ -31,8 +31,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Upgrader diff --git a/setup/upgrade/templates/error.tpl b/setup/upgrade/templates/error.tpl index e7ecb6f..2be6b5f 100644 --- a/setup/upgrade/templates/error.tpl +++ b/setup/upgrade/templates/error.tpl @@ -3,7 +3,7 @@ KnowledgeTree Installer - + @@ -18,7 +18,7 @@
-
+
-
+
-
+

-

Services

@@ -24,7 +23,6 @@
To start the services, using the instructions below: -

@@ -34,17 +32,22 @@
[START MENU] => [Programs] => [KnowledgeTree 3.7.x] => [services]
- Right-Click and run [Install Services] as administrator + Right-Click and run [Install Services] as administrator, if applicable, or +
+ Click [Install Services]
[START MENU] => [Programs] => [KnowledgeTree 3.7.x] => [services]
- Right-Click and run [Start Services] as administrator + Right-Click and run [Start Services] as administrator, if applicable, or +
+ Click [Start Services]

Alternatively:
Open a command prompt and enter the following:
+
cd
dmsctl.bat install
dmsctl.bat start @@ -154,7 +157,6 @@
-
@@ -207,10 +209,8 @@ $redirect = "http://".$_SERVER['SERVER_NAME'].":$port".$root_url."/admin.php"; ?> - - diff --git a/setup/wizard/templates/error.tpl b/setup/wizard/templates/error.tpl index 92fb614..10f2819 100644 --- a/setup/wizard/templates/error.tpl +++ b/setup/wizard/templates/error.tpl @@ -3,7 +3,7 @@ KnowledgeTree Installer - + @@ -19,7 +19,7 @@
-
+
diff --git a/setup/wizard/templates/registration.tpl b/setup/wizard/templates/registration.tpl index ff9ca0c..4b4e560 100644 --- a/setup/wizard/templates/registration.tpl +++ b/setup/wizard/templates/registration.tpl @@ -1,11 +1,12 @@
- +

Registering KnowledgeTree

- Register your KnowledgeTree installation and receive the KnowledgeTree Drop Box for Windows software, a drag and drop tool that makes placing documents into KnowledgeTree even easier. + Register your KnowledgeTree installation. + Skip Registration @@ -22,15 +23,15 @@

- - + + - - - - + + + + @@ -41,6 +42,10 @@ + + + + + + + + +
   image('dropbox.png'); ?>
diff --git a/setup/wizard/templates/services.tpl b/setup/wizard/templates/services.tpl index 0e62f0f..c737c50 100644 --- a/setup/wizard/templates/services.tpl +++ b/setup/wizard/templates/services.tpl @@ -45,7 +45,7 @@     ' style="float:none;"/>     - Submit + Submit

@@ -67,7 +67,7 @@     ' style="float:none;"/>     - Submit + Submit
diff --git a/setup/wizard/templates/wizard.tpl b/setup/wizard/templates/wizard.tpl index 502efe0..e1d1209 100644 --- a/setup/wizard/templates/wizard.tpl +++ b/setup/wizard/templates/wizard.tpl @@ -3,7 +3,7 @@ KnowledgeTree Installer - js('jquery.js'); ?> + tpjs('jquery-1.3.2.js'); ?> js('jquery.form.js'); ?> js('jquery.blockUI.js'); ?> js('jquery.hotkeys.js'); ?> @@ -25,7 +25,7 @@
-
+
@@ -333,7 +333,7 @@ {/if} - {i18n}© 2008, 2009 KnowledgeTree Inc.{/i18n} + {i18n}© 2008-2010 KnowledgeTree Inc.{/i18n} {i18n}All rights reserved.{/i18n} {if ($smallVersion == 'Community Edition')}
{i18n arg_timing=$page->getReqTime()}Request created in #timing#s{/i18n} diff --git a/templates/ktcore/branding/list.smarty b/templates/ktcore/branding/list.smarty new file mode 100755 index 0000000..a4b6f72 --- /dev/null +++ b/templates/ktcore/branding/list.smarty @@ -0,0 +1,5 @@ +

{i18n}Manage Branding{/i18n}

+ +{foreach item=oForm from=$oForms} +{$oForm->render()} +{/foreach} diff --git a/templates/ktcore/forms/widgets/imageselect.smarty b/templates/ktcore/forms/widgets/imageselect.smarty index c5b281a..7173055 100755 --- a/templates/ktcore/forms/widgets/imageselect.smarty +++ b/templates/ktcore/forms/widgets/imageselect.smarty @@ -2,32 +2,17 @@
- {foreach key=id item=src from=$value} + + + + + -
- -
- - - {/foreach} + {foreach key=id item=src from=$value} +
+ +
+ {/foreach}
diff --git a/templates/ktcore/login.smarty b/templates/ktcore/login.smarty index ed0968a..6c97c03 100644 --- a/templates/ktcore/login.smarty +++ b/templates/ktcore/login.smarty @@ -68,7 +68,7 @@
{i18n arg_appname="$appname"}#appname# Version{/i18n} {$versionName}
{i18n}Document Management Software{/i18n}
- {i18n}© 2008, 2009 KnowledgeTree Inc.{/i18n}

+ {i18n}© 2008-2010 KnowledgeTree Inc.{/i18n}

{if ($smallVersion == 'Community Edition')} {i18n}This program is free software and published under the GNU General Public License version 3{/i18n}
{else} diff --git a/templates/ktcore/principals/about.smarty b/templates/ktcore/principals/about.smarty index 56e1ca9..8710b49 100644 --- a/templates/ktcore/principals/about.smarty +++ b/templates/ktcore/principals/about.smarty @@ -1,22 +1,22 @@

{i18n arg_appname="$appname" arg_versionname="$versionname"}#appname# #versionname#{/i18n}

{i18n arg_version="$versionnumber"}Version #version#{/i18n}

-

{i18n}© 2008, 2009 KnowledgeTree Inc.{/i18n}       http://www.knowledgetree.com
+

{i18n}© 2008-2010 KnowledgeTree Inc.{/i18n}       http://www.knowledgetree.com
{if ($smallVersion !== 'Community Edition')} {i18n}All rights reserved.{/i18n}
{/if}
{if ($smallVersion == 'Community Edition')} {i18n}This program is free software and published under the GNU General Public License version 3{/i18n}

- {i18n}KnowledgeTree Community Edition is supplied with no support, - no maintenance, + {i18n}KnowledgeTree Community Edition is supplied with no support, + no maintenance, and no warranty.{/i18n}
{i18n}Please contact the KnowledgeTree Sales team should you wish to learn more about commercially supported editions of KnowledgeTree.{/i18n}
{else}
{i18n}This is a professionally supported edition of KnowledgeTree.{/i18n}
{i18n}Please refer to the documentation provided to you at subscription to learn more about how to access KnowledgeTree's professional support team.{/i18n}
- + {/if}


@@ -32,47 +32,47 @@

Thanks to the following contributors for helping us with code contributions and testing...

- Fu Bin, - Blackhold, - Chris Buechler, - Guillaume Dufloux, - David Alfonso Faspar, - Ruud Feringa, - Hilário Fernandes, - William Hawkins, - Lody Hoekstra, - Kenny Horan, - Artur Kiwa, - Michael Knight, - Jeongkyu Kim, - Rogerio Kohler, - Piotr Krawiecki , - Ola Larsson, - Pavel Lastovicka, - Michel Loiseleur , - Renat Lumpau, - Marco Lusini, - Michael Malone, - Jochem Meijers, - John Miles, - Ismail Mogal, - David Nalley, - Marco Napetti, - Manuela Patrono, - Antti Poro, - Nicolas Quienot, - Ken Rainsforth, - Antonio Rizzelli, - Greg Rundlett, - Leonardo Russo, - Ricardo Silva, - Mario Steinhoff, - Phillip Steinbachs, - Tahir Tahang, - Paul Trgina, - Harry Tsio, - Bjarte Kalstveit Vebjørnsen, - Zakariah, + Fu Bin, + Blackhold, + Chris Buechler, + Guillaume Dufloux, + David Alfonso Faspar, + Ruud Feringa, + Hilário Fernandes, + William Hawkins, + Lody Hoekstra, + Kenny Horan, + Artur Kiwa, + Michael Knight, + Jeongkyu Kim, + Rogerio Kohler, + Piotr Krawiecki , + Ola Larsson, + Pavel Lastovicka, + Michel Loiseleur , + Renat Lumpau, + Marco Lusini, + Michael Malone, + Jochem Meijers, + John Miles, + Ismail Mogal, + David Nalley, + Marco Napetti, + Manuela Patrono, + Antti Poro, + Nicolas Quienot, + Ken Rainsforth, + Antonio Rizzelli, + Greg Rundlett, + Leonardo Russo, + Ricardo Silva, + Mario Steinhoff, + Phillip Steinbachs, + Tahir Tahang, + Paul Trgina, + Harry Tsio, + Bjarte Kalstveit Vebjørnsen, + Zakariah, Jaime Zarate,

And the KnowledgeTree team who "dogfood" KnowledgeTree every day. @@ -80,4 +80,4 @@

This software utilizes third-party software from Pear, PHPMailer, Smarty Template Engine, JSCalendar, Mochikit, Moxiecode Systems, Yahoo Developer Network.

- + diff --git a/templates/ktcore/workflow/admin/effects_overview.smarty b/templates/ktcore/workflow/admin/effects_overview.smarty index 394fb69..891988b 100644 --- a/templates/ktcore/workflow/admin/effects_overview.smarty +++ b/templates/ktcore/workflow/admin/effects_overview.smarty @@ -1,7 +1,7 @@

{i18n arg_name=$workflow_name}Workflow Effects Overview: #name#{/i18n}

{i18n}As a document moves through a workflow, it can cause -varies other actions to occur. For example, you can attach a "Move" action to a transition, +various other actions to occur. For example, you can attach a "Move" action to a transition, which will cause any document moving through that workflow to be moved to a particular folder. Or you can specify that when a document reaches the "Pending Review" state, users with the role "Reviewer" on that document are informed.{/i18n}

diff --git a/tests/api/testApi.php b/tests/api/testApi.php index 9f4c449..6fabc03 100644 --- a/tests/api/testApi.php +++ b/tests/api/testApi.php @@ -295,6 +295,7 @@ class APITestCase extends KTUnitTestCase { * This method tests the retrieval of a folder by name * */ + /* replaced by the new function below public function testGetFolderByName() { $folder = $this->ktapi->get_folder_by_name('Root Folder'); @@ -303,6 +304,7 @@ class APITestCase extends KTUnitTestCase { $this->assertIsA($folder, 'KTAPI_Folder'); $this->assertNoErrors(); } + */ /** * This method tests the retrieval of a document by it's id @@ -607,6 +609,181 @@ class APITestCase extends KTUnitTestCase { $detail2 = $this->ktapi->get_folder_detail($folder_id); $this->assertNotEqual($detail2['status_code'], 0); } + + /** + * Tests finding of a folder or folder detail by name + * + * Runs the following sub-tests: + * + * . Root folder Folder by Name (root folder test) + * . Folder Detail by Name in root folder (no duplicate names) + * . Folder Detail by name in subfolder of root folder (no duplicate names) + * . Folder by Name in subfolder of root folder (no duplicate names) + * . Folder by Name in root folder (duplicate names) + * . Folder Detail by name in subfolder of root folder (duplicate names) + * . Folder by name in subfolder of root folder (duplicate names) + */ + public function testGetFolderByName() + { + // set up + $root_folder_id = array(); + $sub_folder_id = array(); + $folders[0][1] = 'Root Folder'; + + // Create a sub folder in the root folder + $parentId = 1; + $folderName = 'Test api sub-folder ONE'; + $result1 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $root_folder_id[] = $result1['results']['id']; + $folders[$parentId][$result1['results']['id']] = $folderName; + $this->assertEqual($result1['status_code'], 0); + + // Create a second sub folder in the root folder + $parentId = 1; + $folderName = 'Test api sub-folder TWO'; + $result1 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $root_folder_id[] = $result1['results']['id']; + $folders[$parentId][$result1['results']['id']] = $folderName; + $this->assertEqual($result1['status_code'], 0); + + // Create a sub folder in the first sub folder + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder THREE'; + $result2 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $sub_folder_id[0][] = $result2['results']['id']; + $folders[$parentId][$result2['results']['id']] = $folderName; + $this->assertEqual($result2['status_code'], 0); + + // Create a sub folder within the first sub folder which shares a name with one of the root sub folders + $parentId = $sub_folder_id[0][0]; + $folderName = 'Test api sub-folder TWO'; + $result2 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $sub_folder_id[0][] = $result2['results']['id']; + $folders[$parentId][$result2['results']['id']] = $folderName; + $this->assertEqual($result2['status_code'], 0); + + // Create a second sub folder in the first sub folder + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + $result2 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $sub_folder_id[0][] = $result2['results']['id']; + $folders[$parentId][$result2['results']['id']] = $folderName; + $this->assertEqual($result2['status_code'], 0); + + // Create a sub folder within the second sub folder + $parentId = $root_folder_id[1]; + $folderName = 'Test api sub-folder FIVE'; + $result2 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $sub_folder_id[1][] = $result2['results']['id']; + $folders[$parentId][$result2['results']['id']] = $folderName; + $this->assertEqual($result2['status_code'], 0); + + // Create a sub folder within the second sub folder which shares a name with a sub folder in the first sub folder + $parentId = $sub_folder_id[1][0]; + $folderName = 'Test api sub-folder THREE'; + $result2 = $this->ktapi->create_folder($parentId, $folderName, KT_TEST_USER, KT_TEST_PASS, 'Testing API'); + $sub_folder_id[1][] = $result2['results']['id']; + $folders[$parentId][$result2['results']['id']] = $folderName; + $this->assertEqual($result2['status_code'], 0); + + // NOTE default parent is 1, so does not need to be declared when searching the root folder, but we use it elsewhere + + // Fetching of root folder - this used to be in the other test + $parentId = 0; + $folderName = 'Root Folder'; + // no parent required + $folder = $this->ktapi->get_folder_by_name($folderName); + $this->assertNotNull($folder); + $this->assertIsA($folder, 'KTAPI_Folder'); + $this->assertNoErrors(); + // confirm folder id matches expected + if (is_a($folder, 'KTAPI_Folder')) { + $this->assertEqual($folders[$parentId][$folder->get_folderid()], $folderName); + } + + // Folder Detail by Name in root folder (no duplicate names) + $parentId = 1; + $folderName = 'Test api sub-folder ONE'; + // no parent required + $response = $this->ktapi->get_folder_detail_by_name($folderName); + $this->assertNotNull($response['results']); + $this->assertNoErrors(); + // confirm folder id matches expected + if (!empty($response)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder Detail by Name in sub folder of root folder (no duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + // no parent required + $response = $this->ktapi->get_folder_detail_by_name($folderName, $parentId); + $this->assertNotNull($response['results']); + $this->assertNoErrors(); + // confirm folder id matches expected + if (!empty($response)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder by Name in subfolder of root folder (no duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + $folder = $this->ktapi->get_folder_by_name($folderName, $parentId); + $this->assertNotNull($folder); + $this->assertIsA($folder, 'KTAPI_Folder'); + $this->assertNoErrors(); + // confirm folder id matches expected + if (is_a($folder, 'KTAPI_Folder')) { + $this->assertEqual($folders[$parentId][$folder->get_folderid()], $folderName); + } + + // Folder by Name in root folder (duplicate names) + $parentId = 1; + $folderName = 'Test api sub-folder TWO'; + // no parent required + $folder = $this->ktapi->get_folder_by_name($folderName); + $this->assertNotNull($folder); + $this->assertIsA($folder, 'KTAPI_Folder'); + $this->assertNoErrors(); + // confirm folder id matches expected + if (is_a($folder, 'KTAPI_Folder')) { + $this->assertEqual($folders[$parentId][$folder->get_folderid()], $folderName); + } + + // Folder Detail by Name in sub folder of root folder (duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder THREE'; + $response = $this->ktapi->get_folder_detail_by_name($folderName, $parentId); + $this->assertNotNull($response['results']); + $this->assertNoErrors(); + // confirm folder id matches expected + if (!empty($response)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder by Name in sub folder of sub folder (duplicate names) + $parentId = $sub_folder_id[0][0]; + $folderName = 'Test api sub-folder TWO'; + $folder = $this->ktapi->get_folder_by_name($folderName, $parentId); + $this->assertNotNull($folder); + $this->assertIsA($folder, 'KTAPI_Folder'); + $this->assertNoErrors(); + // confirm folder id matches expected + if (is_a($folder, 'KTAPI_Folder')) { + $this->assertEqual($folders[$parentId][$folder->get_folderid()], $folderName); + } + + // Clean up - delete all of the folders + foreach ($root_folder_id as $folder_id) { + $this->ktapi->delete_folder($folder_id, 'Testing API', KT_TEST_USER, KT_TEST_PASS); + } + + foreach ($sub_folder_id as $_folder_id_) { + foreach ($_folder_id_ as $folder_id) { + $this->ktapi->delete_folder($folder_id, 'Testing API', KT_TEST_USER, KT_TEST_PASS); + } + } + } /** * Helper function to create a document diff --git a/tests/env.php b/tests/env.php new file mode 100755 index 0000000..e5dc117 --- /dev/null +++ b/tests/env.php @@ -0,0 +1,34 @@ +TestSuite('Unit tests (Environment)'); + + // Test PHP Version + + $this->addFile('env/testPhpVersion.php'); + + // Test PHP Extensions + $this->addFile('env/testPhpExtensions.php'); + + // Test PHP Configurations + $this->addFile('env/testPhpConfigurations.php'); + + // Test System Configurations + $this->addFile('env/testSystemConfigurations.php'); + } +} + +$test = &new UnitTests(); +if (SimpleReporter::inCli()) { + exit ($test->run(new KTTextReporter()) ? 0 : 1); +} + +// pass parameter ?show=all to display all passes +$param = (isset($_REQUEST['show']) && $_REQUEST['show'] == 'all') ? true : false; +$test->run(new KTHtmlReporter($param)); + +?> diff --git a/tests/env/testPhpConfigurations.php b/tests/env/testPhpConfigurations.php new file mode 100755 index 0000000..24cdf75 --- /dev/null +++ b/tests/env/testPhpConfigurations.php @@ -0,0 +1,357 @@ +oDepends = new dependencies(); + $this->arrConfigRecommended = $this->oDepends->getConfigurations(); + $this->arrConfigLimits = $this->oDepends->getLimits(); + $this->arrConfigActual = $this->oDepends->checkPhpConfiguration(); + } + + /** + * Cleaning up + */ + function tearDown() { + $this->oDepends = null; + } + + function getRecommendedConfig($key) { + foreach ($this->arrConfigRecommended as $conf) { + if ($conf['configuration'] == $key) { + return $conf; + } + } + + foreach ($this->arrConfigLimits as $conf) { + if ($conf['configuration'] == $key) { + return $conf; + } + } + + return false; + } + + + function getActualConfig($key) { + foreach ($this->arrConfigActual as $conf) { + if ($conf['configuration'] == $key) { + return $conf; + } + } + return false; + } + + /** + * Testing safeMode + */ + function testSafeMode() { + $key = 'safe_mode'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing fileUploads + */ + function testFileUploads() { + $key = 'file_uploads'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing magicQuotesGPC + */ + function testMagicQuotesGPC() { + $key = 'magic_quotes_gpc'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing magicQuotesRuntime + */ + function testMagicQuotesRuntime() { + $key = 'magic_quotes_runtime'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing registerGlobals + */ + function testRegisterGlobals() { + $key = 'register_globals'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing outputBuffering + */ + function testOutputBuffering() { + $key = 'output_buffering'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing sessionAutoStart + */ + function testSessionAutoStart() { + $key = 'session.auto_start'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing automaticPrependFile + */ + function testAutomaticPrependFile() { + $key = 'auto_prepend_file'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing automaticAppendFile + */ + function testAutomaticAppendFile() { + $key = 'auto_append_file'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing openBaseDirectory + */ + function testOpenBaseDirectory() { + $key = 'open_basedir'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + /** + * Testing defaultMimetype + */ + function testDefaultMimetype() { + $key = 'default_mimetype'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + $this->assertEqual($actualConfig['setting'], $requiredConfig['recommended']); + } + + function to_byte($size) { + $res = preg_match_all('/[a-z]/isU', $size, $matches, PREG_PATTERN_ORDER); + $indicator = $matches[0][0]; + if ($indicator == '') return false; + + switch ($indicator) { + case 'B': + return $size; + case 'KB': + return $size * (1024); + case 'K': + return $size * (1024); + case 'M': + return $size * (1024 * 1024); + case 'MB': + return $size * (1024 * 1024); + case 'G': + return $size * (1024 * 1024 * 1024); + case 'GB': + return $size * (1024 * 1024 * 1024); + case 'T': + return $size * (1024 * 1024 * 1024 * 1024); + case 'TB': + return $size * (1024 * 1024 * 1024 * 1024); + case 'P': + return $size * (1024 * 1024 * 1024 * 1024 * 1024); + case 'PB': + return $size * (1024 * 1024 * 1024 * 1024 * 1024); + case 'E': + return $size * (1024 * 1024 * 1024 * 1024 * 1024 * 1024); + case 'EB': + return $size * (1024 * 1024 * 1024 * 1024 * 1024 * 1024); + case 'Z': + return $size * (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024); + case 'ZB': + return $size * (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024); + case 'Y': + return $size * (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024); + case 'YB': + return $size * (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024); + default: + return $size; + + } + + //return $size ? round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $filesizename[$i] : '0 Bytes'; + } + + /** + * Testing maximumPostSize + */ + function testMaximumPostSize() { + $key = 'post_max_size'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + + $byteSetting = $this->to_byte($actualConfig['setting']); + $byteRecommended = $this->to_byte($requiredConfig['recommended']); + //Testing that the byte conversion passed + $this->assertNotEqual($byteSetting, false, 'Could Not Convert Actual Setting to Bytes [' . $actualConfig['setting'] . ']'); + $result = ($byteSetting >= $byteRecommended); + $this->assertTrue($result, 'current php.ini conf value for '.$key.' ['.$actualConfig['setting'].'] is too small. The recommended size is ' . $requiredConfig['recommended']); + } + + /** + * Testing maximumUploadSize + */ + function testMaximumUploadSize() { + if (!ini_get('upload_max_filesize') == -1) { + $key = 'upload_max_filesize'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + + $byteSetting = $this->to_byte($actualConfig['setting']); + $byteRecommended = $this->to_byte($requiredConfig['recommended']); + //Testing that the byte conversion passed + $this->assertNotEqual($byteSetting, false, 'Could Not Convert Actual Setting to Bytes [' . $actualConfig['setting'] . ']'); + $result = ($byteSetting >= $byteRecommended); + $this->assertTrue($result, 'current php.ini conf value for '.$key.' ['.$actualConfig['setting'].'] is too small. The recommended size is ' . $requiredConfig['recommended']); + } + } + + /** + * Testing memoryLimit + */ + function testMemoryLimit() { + if (!ini_get('memory_limit') == -1) { + $key = 'memory_limit'; + $actualConfig = $this->getActualConfig($key); + $requiredConfig = $this->getRecommendedConfig($key); + + $byteSetting = $this->to_byte($actualConfig['setting']); + $byteRecommended = $this->to_byte($requiredConfig['recommended']); + //Testing that the byte conversion passed + $this->assertNotEqual($byteSetting, false, 'Could Not Convert Actual Setting to Bytes [' . $actualConfig['setting'] . ']'); + $result = ($byteSetting >= $byteRecommended); + $this->assertTrue($result, 'current php.ini conf value for '.$key.' ['.$actualConfig['setting'].'] is too small. The recommended size is ' . $requiredConfig['recommended']); + } + } + +} +?> \ No newline at end of file diff --git a/tests/env/testPhpExtensions.php b/tests/env/testPhpExtensions.php new file mode 100755 index 0000000..48e5f3e --- /dev/null +++ b/tests/env/testPhpExtensions.php @@ -0,0 +1,153 @@ +oDepends = new dependencies(); + $this->arrRequiredExtensions = $this->oDepends->getRequiredExtensions(); + } + + /** + * Cleaning up + */ + function tearDown() { + $this->oDepends = null; + } + + /** + * Testing extensions + */ + function testExtensions() { + foreach ($this->arrRequiredExtensions as $ext) { + $ext['available'] = 'no'; + if($this->oDepends->checkExtension($ext['extension'])){ + $ext['available'] = 'yes'; + } else { + if($ext['required'] == 'no') { + $errorMsg = '['.$ext['extension'].'] Missing optional extension: '.$ext['name']; + } else { + $errorMsg = '['.$ext['extension'].'] Missing required extension: '.$ext['name']; + } + } + $this->assertEqual($ext['available'], 'yes', $errorMsg); + } + } + + + public function getExtension($key) { + foreach ($this->arrRequiredExtensions as $ext) { + if ($ext['extension'] == $key) { + return $ext; + } + } + return false; + } + + /** + * Testing IconV + */ + function testIconV() { + $key = 'iconv'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing optional extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + /** + * Testing MySQL + */ + function testMySQL() { + $key = 'mysql'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing required extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + /** + * Testing cURL + */ + function testcURL() { + $key = 'curl'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing required extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + /** + * Testing XMLRPC + */ + function testXMLRPC() { + $key = 'xmlrpc'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing required extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + + /** + * Testing Multi Byte Strings + */ + function testMultiByteStrings() { + $key = 'mbstring'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing optional extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + /** + * Testing LDAP + */ + function testLDAP() { + $key = 'ldap'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing optional extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + /** + * Testing JSON + */ + function testJSON() { + $key = 'json'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing required extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + + /** + * Testing Open SSL + */ + function testOpenSSL() { + $key = 'openssl'; + $ext = $this->getExtension($key); + $errorMsg = '['.$ext['extension'].'] Missing optional extension: '.$ext['name']; + $this->assertEqual($this->oDepends->checkExtension($ext['extension']), true, $errorMsg); + } + +} +?> \ No newline at end of file diff --git a/tests/env/testPhpVersion.php b/tests/env/testPhpVersion.php new file mode 100755 index 0000000..34d8f26 --- /dev/null +++ b/tests/env/testPhpVersion.php @@ -0,0 +1,53 @@ +oDepends = new dependencies(); + } + + /** + * Cleanup + * + */ + function tearDown() { + $this->oDepends = null; + } + + /** + * Testing PHP Version + */ + function testPhpVersion() + { + $this->phpVersion = $this->oDepends->checkPhpVersion(); + $this->assertEqual($this->phpVersion['class'], 'tick'); + } + +} +?> \ No newline at end of file diff --git a/tests/env/testSystemConfigurations.php b/tests/env/testSystemConfigurations.php new file mode 100644 index 0000000..6ac2915 --- /dev/null +++ b/tests/env/testSystemConfigurations.php @@ -0,0 +1,102 @@ +oConfig = new configuration(); + $this->arrServerInfo = $this->oConfig->getServerInfo(); + $this->arrPathInfo = $this->oConfig->getPathInfo($this->arrServerInfo['file_system_root']['value'], false); + } + + /** + * Cleaning up + */ + function tearDown() { + $this->oConfig = null; + } + + /** + * Testing configFile + */ + function testConfigFile() { + $key = 'configFile'; + $fileValid = (file_exists($this->arrPathInfo[$key]['path'])); + $this->assertTrue($fileValid, 'Config file : [' . $this->arrPathInfo[$key]['path'] . '] could not be found.'); + } + + /** + * Testing documentRoot + */ + function testDocumentRoot() { + $key = 'documentRoot'; + $this->assertEqual($this->arrPathInfo[$key]['class'], 'tick', $this->arrPathInfo[$key]['msg']); + } + + /** + * Testing logDirectory + */ + function testLogDirectory() { + $key = 'logDirectory'; + $this->assertEqual($this->arrPathInfo[$key]['class'], 'tick', $this->arrPathInfo[$key]['msg']); + } + + /** + * Testing tmpDirectory + */ + function testTmpDirectory() { + $key = 'tmpDirectory'; + $this->assertEqual($this->arrPathInfo[$key]['class'], 'tick', $this->arrPathInfo[$key]['msg']); + } + + /** + * Testing cacheDirectory + */ + function testCacheDirectory() { + $key = 'cacheDirectory'; + $this->assertEqual($this->arrPathInfo[$key]['class'], 'tick', $this->arrPathInfo[$key]['msg']); + } + + /** + * Testing uploadDirectory + */ + function testUploadDirectory() { + $key = 'uploadDirectory'; + $this->assertEqual($this->arrPathInfo[$key]['class'], 'tick', $this->arrPathInfo[$key]['msg']); + } + + /** + * Testing varDirectory + */ + function testvarDirectory() { + $key = 'varDirectory'; + $this->assertEqual($this->arrPathInfo[$key]['class'], 'tick', $this->arrPathInfo[$key]['msg']); + } + +} +?> \ No newline at end of file diff --git a/tests/ktcmis/testCmisApi.php b/tests/ktcmis/testCmisApi.php index 49d0481..d2cd9fd 100644 --- a/tests/ktcmis/testCmisApi.php +++ b/tests/ktcmis/testCmisApi.php @@ -62,7 +62,9 @@ class CMISTestCase extends KTUnitTestCase { * This method ends the KT session */ public function tearDown() { - $this->session->logout(); + if (!is_null($this->session) && !PEAR::isError($this->session)) { + $this->session->logout(); + } } // Repository service functions @@ -529,7 +531,7 @@ class CMISTestCase extends KTUnitTestCase { $response = $NavigationService->getCheckedOutDocs($repositoryId, false, false); $this->assertEqual($response['status_code'], 0); $this->assertNotNull($response['results']); - $this->assertTrue($this->findInPropertiesArray('ObjectId', $documentId, $response['results'])); + $this->assertTrue($this->findInPropertiesArray('objectId', $documentId, $response['results'])); // now let's cancel the checkout so that we can delete later during cleanup :) $response = $VersioningService->cancelCheckOut($repositoryId, $pwcId); diff --git a/tests/runtests.php b/tests/runtests.php index be73a73..f418c46 100644 --- a/tests/runtests.php +++ b/tests/runtests.php @@ -8,7 +8,7 @@ class UnitTests extends TestSuite { $this->TestSuite('Unit tests'); // CMIS API - $this->addFile('ktcmis/testCmisApi.php'); +// $this->addFile('ktcmis/testCmisApi.php'); // KTAPI // Some of these tests will fail if Electronic Signatures are enabled for the API. @@ -36,7 +36,11 @@ class UnitTests extends TestSuite { // if Electronic Signatures are NOT enabled for the API, new tests may not // include the check which allows the tests to be bypassed when esignatures // are not on, so if you have failures, check there first :) - $this->addFile('api/testElectronicSignatures.php'); +// $this->addFile('api/testElectronicSignatures.php'); + + // Web Service tests + $this->addFile('webservices/testRest.php'); + $this->addFile('webservices/testSoap.php'); // $this->addFile('SQLFile/test_sqlfile.php'); // $this->addFile('cache/testCache.php'); @@ -48,9 +52,9 @@ class UnitTests extends TestSuite { // $this->addFile('filelike/testStringFileLike.php'); // Search (2) and indexing -// $this->addFile('documentProcessor/testExtracters.php'); + $this->addFile('documentProcessor/testExtracters.php'); // $this->addFile('documentProcessor/testGuidInserter.php'); -// $this->addFile('search2/testSearch.php'); + $this->addFile('search2/testSearch.php'); } } diff --git a/tests/webservices/testRest.php b/tests/webservices/testRest.php index 8261613..6fdf14c 100644 --- a/tests/webservices/testRest.php +++ b/tests/webservices/testRest.php @@ -137,6 +137,210 @@ class RESTTestCase extends KTUnitTestCase { $this->assertTrue(empty($response['results'])); $this->assertTrue(!empty($response['message'])); } + + /** + * Tests finding of a folder or folder detail by name + * Runs the following sub-tests: + * . Root folder Folder by Name (root folder test) + * . Folder Detail by Name in root folder (no duplicate names) + * . Folder Detail by name in subfolder of root folder (no duplicate names) + * . Folder by Name in subfolder of root folder (no duplicate names) + * . Folder by Name in root folder (duplicate names) + * . Folder Detail by name in subfolder of root folder (duplicate names) + * . Folder by name in subfolder of root folder (duplicate names) + */ + public function testGetFolderByName() + { + // Login and authenticate + $url = $this->rootUrl.'method=login&password=admin&username=admin'; + $response = $this->call($url); + $response = $response['response']; + + $this->assertEqual($response['status_code'], 0); + $session_id = $response['results']; + + // set up + $root_folder_id = array(); + $sub_folder_id = array(); + $folders[0][1] = 'Root Folder'; + + // Create a sub folder in the root folder + $parentId = 1; + $folderName = 'Test api sub-folder ONE'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $root_folder_id[] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // Create a second sub folder in the root folder + $parentId = 1; + $folderName = 'Test api sub-folder TWO'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $root_folder_id[] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // Create a sub folder in the first sub folder + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder THREE'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $sub_folder_id[0][] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // Create a sub folder within the first sub folder which shares a name with one of the root sub folders + $parentId = $sub_folder_id[0][0]; + $folderName = 'Test api sub-folder TWO'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $sub_folder_id[0][] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // Create a second sub folder in the first sub folder + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $sub_folder_id[0][] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // Create a sub folder within the second sub folder + $parentId = $root_folder_id[1]; + $folderName = 'Test api sub-folder FIVE'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $sub_folder_id[1][] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // Create a sub folder within the second sub folder which shares a name with a sub folder in the first sub folder + $parentId = $sub_folder_id[1][0]; + $folderName = 'Test api sub-folder THREE'; + $url = $this->rootUrl.'method=create_folder&session_id=' . $session_id . '&folder_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $sub_folder_id[1][] = $response['results']['id']; + $folders[$parentId][$response['results']['id']] = $folderName; + $this->assertEqual($response['status_code'], 0); + + // NOTE default parent is 1, so does not need to be declared when searching the root folder, but we use it elsewhere + + // Fetching of root folder + $parentId = 0; + $folderName = 'Root Folder'; + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $this->assertEqual($response['status_code'], 0); + $this->assertFalse(!empty($response['message'])); + if (($response['status_code'] == 0)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder Detail by Name in root folder (no duplicate names) + $parentId = 1; + $folderName = 'Test api sub-folder ONE'; + // no parent required + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $this->assertEqual($response['status_code'], 0); + $this->assertFalse(!empty($response['message'])); + if (($response['status_code'] == 0)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder Detail by Name in sub folder of root folder (no duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&parent_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $this->assertEqual($response['status_code'], 0); + $this->assertFalse(!empty($response['message'])); + if (($response['status_code'] == 0)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder Detail by Name in root folder (duplicate names) + $parentId = 1; + $folderName = 'Test api sub-folder TWO'; + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&parent_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $this->assertEqual($response['status_code'], 0); + $this->assertFalse(!empty($response['message'])); + if (($response['status_code'] == 0)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Folder Detail by Name in sub folder of root folder (duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder THREE'; + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&parent_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $this->assertEqual($response['status_code'], 0); + $this->assertFalse(!empty($response['message'])); + if (($response['status_code'] == 0)) { + $this->assertEqual($folders[$parentId][$response['results']['id']], $folderName); + } + + // Negative test with non duplicated folder - look for folder in location it does not exist + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder ONE'; + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&parent_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + $this->assertNotEqual($response['status_code'], 0); + $this->assertTrue(!empty($response['message'])); + $this->assertNotEqual($folders[$parentId][$response['results']['id']], $folderName); + + // Negative test with duplicated folder - look for folder with incorrect parent id, result should not match expected folder + $parentId = 1; + $actualParentId = $sub_folder_id[0][0]; + $folderName = 'Test api sub-folder TWO'; + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&parent_id=' . $parentId . '&folder_name=' . urlencode($folderName); + $response = $this->call($url); + $response = $response['response']; + // we should get a result + $this->assertEqual($response['status_code'], 0); + $this->assertFalse(!empty($response['message'])); + // but not the one we wanted + $url = $this->rootUrl.'method=get_folder_detail_by_name&session_id=' . $session_id . '&parent_id=' . $actualParentId . '&folder_name=' . urlencode($folderName); + $expectedResponse = $this->call($url); + $expectedResponse = $expectedResponse['response']; + $this->assertNotEqual($response['results']['id'], $expectedResponse['results']['id']); + + // Clean up - delete all of the folders + foreach ($root_folder_id as $folder_id) { + $url = $this->rootUrl.'method=delete_folder&session_id=' . $session_id . '&folder_id=' . $folder_id . '&reason=Testing%20Webservice'; + $this->call($url); + } + + foreach ($sub_folder_id as $_folder_id_) { + foreach ($_folder_id_ as $folder_id) { + $url = $this->rootUrl.'method=delete_folder&session_id=' . $session_id . '&folder_id=' . $folder_id . '&reason=Testing%20Webservice'; + $this->call($url); + } + } + + // Logout + $url = $this->rootUrl.'method=logout&session_id='.$session_id; + $response = $this->call($url); + $response = $response['response']; + } /** * Convert xml into an array structure diff --git a/tests/webservices/testSoap.php b/tests/webservices/testSoap.php new file mode 100644 index 0000000..6762d66 --- /dev/null +++ b/tests/webservices/testSoap.php @@ -0,0 +1,914 @@ +rootUrl = $url.'/ktwebservice/webservice.php?'; + $this->user = isset($_GET['user']) ? $_GET['user'] : 'admin'; + $this->pass = isset($_GET['pass']) ? $_GET['pass'] : 'admin'; + + // Login and authenticate + $this->connect(); + $this->login('127.0.0.1'); + } + + /** + * This method is a placeholder + */ + public function tearDown() + { + // Logout + $this->logout(); + } + + private function connect() + { + $wsdl = $this->rootUrl . "wsdl"; + $this->client = new SoapClient($wsdl); + } + + private function login($ip = null) + { + $res = $this->client->__soapCall("login", array($this->user, $this->pass, $ip)); + if($res->status_code != 0){ + return false; + } + $this->session = $res->message; + } + + private function logout() + { + $result = $this->client->__soapCall("logout", array($this->session)); + if($result->status_code != 0){ + return true; + } + } + + private function getDocTypes() + { + $result = $this->client->__soapCall("get_document_types", array($this->session)); + return $result->document_types; + } + + private function search($expr) + { + $result = $this->client->__soapCall("search", array($this->session, $expr, '')); + return $result->hits; + } + + private function getFolderDetail($folder, $parentId = 1) + { + $result = $this->client->__soapCall("get_folder_detail_by_name", array($this->session, $folder, $parentId)); + return $result; + } + + private function createFolder($parent_id, $folder) + { + $result = $this->client->__soapCall("create_folder", array($this->session, $parent_id, $folder)); + return $result; + } + + private function createFolderShortcut($target_folder_id, $source_folder_id) + { + $result = $this->client->__soapCall("create_folder_shortcut", array($this->session, $target_folder_id, $source_folder_id)); + return $result; + } + + private function deleteFolder($folder_id) + { + $result = $this->client->__soapCall("delete_folder", array($this->session, $folder_id, 'Testing')); + return $result; + } + + private function getFolderShortcuts($folder_id) + { + $result = $this->client->__soapCall("get_folder_shortcuts", array($this->session, $folder_id)); + return $result; + } + + private function getDocumentVersionHistory($document_id) + { + $result = $this->client->__soapCall("get_document_version_history", array($this->session, $document_id)); + return $result; + } + + private function createDocument($folder_id, $title, $filename, $documenttype = 'Default') + { + global $default; + $uploads_dir = $default->uploadDirectory; + + // create document in uploads folder + $tempfilename = tempnam($uploads_dir, 'myfile'); + $fp = fopen($tempfilename, 'wt'); + fwrite($fp, $this->content); + fclose($fp); + + // call add document to upload into repo + $result = $this->client->__soapCall("add_document", array($this->session, $folder_id, $title, $filename, $documenttype, $tempfilename)); + return $result; + } + + private function createSmallDocument($folder_id, $title, $filename, $documenttype = 'Default') + { + global $default; + $uploads_dir = $default->uploadDirectory; + + // create document in uploads folder + $base64 = base64_encode($this->content); + + // call add document to upload into repo + $result = $this->client->__soapCall("add_small_document", array($this->session, $folder_id, $title, $filename, $documenttype, $base64)); + return $result; + } + + private function checkinDocument($document_id, $filename, $major_update = false) + { + global $default; + $uploads_dir = $default->uploadDirectory; + + // create document in uploads folder + $tempfilename = tempnam($uploads_dir, 'myfile'); + $fp = fopen($tempfilename, 'wt'); + fwrite($fp, $this->content_update); + fclose($fp); + + // call add document to upload into repo + $result = $this->client->__soapCall("checkin_document", array($this->session, $document_id, $filename, $this->reason, $tempfilename, $major_update)); + return $result; + } + + private function checkinSmallDocument($document_id, $filename, $major_update = false) + { + global $default; + $uploads_dir = $default->uploadDirectory; + + // encode as base64 + $base64 = base64_encode($this->content_update); + + // call add document to upload into repo + $result = $this->client->__soapCall("checkin_small_document", array($this->session, $document_id, $filename, $this->reason, $base64, $major_update)); + return $result; + } + + private function checkoutDocument($document_id, $download = true) + { + $result = $this->client->__soapCall("checkout_document", array($this->session, $document_id, $this->reason, $download)); + return $result; + } + + private function checkoutSmallDocument($document_id, $download = true) + { + $result = $this->client->__soapCall("checkout_small_document", array($this->session, $document_id, $this->reason, $download)); + return $result; + } + + private function createDocumentShortcut($folder_id, $target_document_id) + { + $result = $this->client->__soapCall("create_document_shortcut", array($this->session, $folder_id, $target_document_id)); + return $result; + } + + private function getDocumentShortcuts($document_id) + { + $result = $this->client->__soapCall("get_document_shortcuts", array($this->session, $document_id)); + return $result; + } + + private function getFolderContents($folder_id, $depth=1, $what='DFS') + { + $result = $this->client->__soapCall("get_folder_contents", array($this->session, $folder_id, $depth, $what)); + return $result; + } + + private function getDocumentMetadata($document_id, $version = null) + { + $result = $this->client->__soapCall("get_document_metadata", array($this->session, $document_id, $version)); + return $result; + } + + private function updateDocumentMetadata($document_id, $metadata, $sysdata=null) + { + $result = $this->client->__soapCall("update_document_metadata", array($this->session, $document_id, $metadata, $sysdata)); + return $result; + } + + private function downloadDocument($document_id, $version = null) + { + $result = $this->client->__soapCall("download_document", array($this->session, $document_id, $version)); + return $result; + } + + private function downloadSmallDocument($document_id, $version = null) + { + $result = $this->client->__soapCall("download_small_document", array($this->session, $document_id, $version)); + return $result; + } + + private function doDownload($url) + { + $ch = curl_init($url); + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch,CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch,CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch,CURLOPT_RETURNTRANSFER, true); + + $download = curl_exec($ch); + $status = curl_getinfo($ch,CURLINFO_HTTP_CODE); + + $error = curl_errno($ch); + + curl_close($ch); + + return array('status' => $status, 'error' => $error, 'download' => $download); + } + + /* *** now the test functions *** */ + + /** + * Tests finding of a folder or folder detail by name + * Runs the following sub-tests: + * . Folder Detail by Name in root folder (no duplicate names) + * . Folder Detail by Name in subfolder of root folder (no duplicate names) + * . Folder Detail by Name in subfolder of root folder (duplicate names) + * + * NOTE there are less tests here because we cannot test the get_folder_by_name function since it does not exist within the web service code + */ + public function testGetFolderByName() + { + // set up + $root_folder_id = array(); + $sub_folder_id = array(); + $folders[0][1] = 'Root Folder'; + + // Create a sub folder in the root folder + $parentId = 1; + $folderName = 'Test api sub-folder ONE'; + $response = $this->createFolder($parentId, $folderName); + $root_folder_id[] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // Create a second sub folder in the root folder + $parentId = 1; + $folderName = 'Test api sub-folder TWO'; + $response = $this->createFolder($parentId, $folderName); + $root_folder_id[] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // Create a sub folder in the first sub folder + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder THREE'; + $response = $this->createFolder($parentId, $folderName); + $sub_folder_id[0][] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // Create a sub folder within the first sub folder which shares a name with one of the root sub folders + $parentId = $sub_folder_id[0][0]; + $folderName = 'Test api sub-folder TWO'; + $response = $this->createFolder($parentId, $folderName); + $sub_folder_id[0][] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // Create a second sub folder in the first sub folder + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + $response = $this->createFolder($parentId, $folderName); + $sub_folder_id[0][] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // Create a sub folder within the second sub folder + $parentId = $root_folder_id[1]; + $folderName = 'Test api sub-folder FIVE'; + $response = $this->createFolder($parentId, $folderName); + $sub_folder_id[1][] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // Create a sub folder within the second sub folder which shares a name with a sub folder in the first sub folder + $parentId = $sub_folder_id[1][0]; + $folderName = 'Test api sub-folder THREE'; + $response = $this->createFolder($parentId, $folderName); + $sub_folder_id[1][] = $response->id; + $folders[$parentId][$response->id] = $folderName; + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + + // NOTE default parent is 1, so does not need to be declared when searching the root folder, but we use it elsewhere + + // Fetching of root folder + $parentId = 0; + $folderName = 'Root Folder'; + $response = $this->getFolderDetail($folderName); + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + if (($response->status_code == 0)) { + $this->assertEqual($folders[$parentId][$response->id], $folderName); + } + + // Folder Detail by Name in root folder (no duplicate names) + $parentId = 1; + $folderName = 'Test api sub-folder ONE'; + // no parent required + $response = $this->getFolderDetail($folderName); + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + if (($response->status_code == 0)) { + $this->assertEqual($folders[$parentId][$response->id], $folderName); + } + + // Folder Detail by Name in sub folder of root folder (no duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder FOUR'; + $response = $this->getFolderDetail($folderName, $parentId); + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + if (($response->status_code == 0)) { + $this->assertEqual($folders[$parentId][$response->id], $folderName); + } + + // Folder Detail by Name in root folder (duplicate names) + $parentId = 1; + $folderName = 'Test api sub-folder TWO'; + $response = $this->getFolderDetail($folderName, $parentId); + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + if (($response->status_code == 0)) { + $this->assertEqual($folders[$parentId][$response->id], $folderName); + } + + // Folder Detail by Name in sub folder of root folder (duplicate names) + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder THREE'; + $response = $this->getFolderDetail($folderName, $parentId); + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + if (($response->status_code == 0)) { + $this->assertEqual($folders[$parentId][$response->id], $folderName); + } + + // Negative test with non duplicated folder - look for folder in location it does not exist + $parentId = $root_folder_id[0]; + $folderName = 'Test api sub-folder ONE'; + $response = $this->getFolderDetail($folderName, $parentId); + $this->assertNotEqual($response->status_code, 0); + $this->assertTrue(!empty($response->message)); + $this->assertNotEqual($folders[$parentId][$response->id], $folderName); + + // Negative test with duplicated folder - look for folder with incorrect parent id, result should not match expected folder + $parentId = 1; + $actualParentId = $sub_folder_id[0][0]; + $folderName = 'Test api sub-folder TWO'; + $response = $this->getFolderDetail($folderName, $parentId); + // we should get a result + $this->assertEqual($response->status_code, 0); + $this->assertFalse(!empty($response->message)); + // but not the one we wanted + $expectedResponse = $this->getFolderDetail($folderName, $actualParentId); + $this->assertNotEqual($response->id, $expectedResponse->id); + + // Clean up - delete all of the folders + foreach ($root_folder_id as $folder_id) { + $this->deleteFolder($folder_id); + } + } + + /** + * Tests the creation of folders and folder shortcuts, getting the folder shortcut(s) + * WebService V3 and higher + */ + public function testCreateFolderShortcut() + { + // if V3 + if(LATEST_WEBSERVICE_VERSION < 3){ + return ; + } + + // Create folder 1 + $result = $this->createFolder(1, 'Test Shortcut Container'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $parentFolderId = $result->id; + + // Create folder 2 + $result = $this->createFolder(1, 'Test Shortcut Target'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $targetFolderId = $result->id; + + // Create folder 3 + $result = $this->createFolder($targetFolderId, 'Test Shortcut Target 2'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $target2FolderId = $result->id; + + // Create shortcut in folder 1 targeting folder 2 + $result = $this->createFolderShortcut($parentFolderId, $targetFolderId); + $this->assertEqual($result->status_code, 0, 'Create folder Shortcut - '.$result->message); + + $shortcutId = $result->id; + $linkedFolderId = $result->linked_folder_id; + $this->assertTrue(is_numeric($shortcutId), 'Shortcut id should be numeric'); + $this->assertTrue(is_numeric($linkedFolderId), 'Linked folder id should be numeric'); + $this->assertEqual($linkedFolderId, $targetFolderId, 'Shortcut should contain link the target folder'); + + // Create shortcut in folder 1 targeting folder 3 + $result = $this->createFolderShortcut($parentFolderId, $target2FolderId); + $this->assertEqual($result->status_code, 0, 'Create folder shortcut - '.$result->message); + + $shortcut2Id = $result->id; + $linkedFolder2Id = $result->linked_folder_id; + $this->assertTrue(is_numeric($shortcut2Id), 'Shortcut id should be numeric'); + $this->assertTrue(is_numeric($linkedFolder2Id), 'Linked folder id should be numeric'); + $this->assertEqual($linkedFolder2Id, $target2FolderId, 'Shortcut should contain link the target folder'); + + // Get the folder shortcut details + $result = $this->getFolderShortcuts($targetFolderId); + $this->assertEqual($result->status_code, 0, 'Should return list of folder shortcuts - '.$result->message); + + $shortcuts = $result[1]; + foreach ($shortcuts as $item) { + $this->assertEqual($item->id, $shortcutId); + $this->assertEqual($item->linked_folder_id, $targetFolderId); + } + + $result = $this->getFolderShortcuts($target2FolderId); + $this->assertEqual($result->status_code, 0, 'Should return list of folder shortcuts - '.$result->message); + + $shortcuts2 = $result[1]; + foreach ($shortcuts2 as $item) { + $this->assertEqual($item->id, $shortcut2Id); + $this->assertEqual($item->linked_folder_id, $target2FolderId); + } + + // delete and cleanup + $this->deleteFolder($parentFolderId); + $this->deleteFolder($targetFolderId); + } + + /** + * Tests the document upload and the creation and retrieval of document shortcuts + * WS V3 + */ + public function testCreateDocumentShortcut() + { + // if V3 + if(LATEST_WEBSERVICE_VERSION < 3){ + //$this->assertEqual(LATEST_WEBSERVICE_VERSION, 3, 'Webservice version is less than 3. Exiting test, functionality is V3 only'); + return ; + } + + // Create folder 1 containing the shortcut + $result = $this->createFolder(1, 'Test Shortcut Container'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $parentFolderId = $result->id; + + // Create folder 2 containing the document + $result = $this->createFolder(1, 'Test Shortcut Target'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $targetFolderId = $result->id; + + // Upload document to folder 2 + $result = $this->createDocument($targetFolderId, 'Target Doc', 'target.txt'); + $this->assertEqual($result->status_code, 0, 'Create document - '.$result->message); + $documentId = $result->document_id; + $this->assertTrue(is_numeric($documentId), 'Document id should be numeric'); + + // Create shortcut in folder 1, pointing to the document in folder 2 + $result = $this->createDocumentShortcut($parentFolderId, $documentId); + $this->assertEqual($result->status_code, 0, 'Create document shortcut - '.$result->message); + $shortcutId = $result->document_id; + $linkedDocumentId = $result->linked_document_id; + $this->assertTrue(is_numeric($shortcutId), 'Shortcut id should be numeric'); + $this->assertTrue(is_numeric($linkedDocumentId), 'Linked document id should be numeric'); + $this->assertEqual($linkedDocumentId, $documentId, 'Shortcut should contain link to the target document'); + + // delete and cleanup + $this->deleteFolder($parentFolderId); + $this->deleteFolder($targetFolderId); + } + + /** + * Tests document uploads, checkin / checkout, downloads and document version downloads (V3) + * Normal documents and small / base64 documents + */ + public function testUploadDownloadDocument() + { + // Create folder containing the documents + $result = $this->createFolder(1, 'Test Container'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $folderId = $result->id; + + // Upload document to folder + $filename = 'test_doc.txt'; + $result = $this->createDocument($folderId, 'Test Doc', $filename); + $this->assertEqual($result->status_code, 0, 'Create document - '.$result->message); + $documentId = $result->document_id; + $this->assertTrue(is_numeric($documentId), 'Document id should be numeric'); + + // Get the download url for the document + $result = $this->downloadDocument($documentId); + $this->assertEqual($result->status_code, 0, 'Get document download url - '.$result->message); + $url = $result->message; + $this->assertTrue(is_string($url), 'URL should be a string'); + $this->assertTrue(strpos($url, 'http') !== false, 'URL should contain http'); + + // Download the document + $result = $this->doDownload($url); + $this->assertEqual($result['status'], 200, 'Status should be code 200 for success - '.$result['status']); + $this->assertEqual($result['error'], 0, 'Error code should be 0 for success - '.$result['error']); + $this->assertEqual($result['download'], $this->content, 'Document content should be the same as that uploaded'); + + // ---------- + + // Upload base64 document to folder + $filename2 = 'test_base64_doc.txt'; + $result = $this->createSmallDocument($folderId, 'Test Base64 Doc', $filename2); + $this->assertEqual($result->status_code, 0, 'Create base64 document - '.$result->message); + $documentId2 = $result->document_id; + $this->assertTrue(is_numeric($documentId2), 'Document id should be numeric'); + + // Download base64 document + $result = $this->downloadSmallDocument($documentId2); + $this->assertEqual($result->status_code, 0, 'Download small document - '.$result->message); + $content = $result->message; + $this->assertTrue(is_string($content), 'Content should be a string'); + $content = base64_decode($content); + $this->assertEqual($content, $this->content, 'Document content should be the same as that uploaded'); + + if(LATEST_WEBSERVICE_VERSION < 3){ + // delete and cleanup + $this->deleteFolder($folderId); + return ; + } + + /* *** V3 functions *** */ + + // Checkout first document + $result = $this->checkoutDocument($documentId); + $this->assertEqual($result->status_code, 0, 'Checkout document - '.$result->message); + $checkoutBy = $result->checked_out_by; + $checkoutDate = $result->checked_out_date; + $url = $result->message; + $this->assertFalse(is_null($checkoutDate), 'Checked out date should not be null / empty - '.$checkoutDate); + $this->assertEqual($checkoutBy, 'Administrator', 'Using the Administrative user, checked out user must match - '.$checkoutBy); + $this->assertTrue(is_string($url), 'Download URL should be a string'); + $this->assertTrue(strpos($url, 'http') !== false, 'Download URL should contain http'); + + // Download the document + $result = $this->doDownload($url); + $content = $result['download']; + $this->assertEqual($result['status'], 200, 'Status should be code 200 for success - '.$result['status']); + $this->assertEqual($result['error'], 0, 'Error code should be 0 for success - '.$result['error']); + $this->assertEqual($result['download'], $this->content, 'Document content should be the same as that uploaded'); + + // ---------- + + // Checkin a new version + $result = $this->checkinDocument($documentId, $filename); + $this->assertEqual($result->status_code, 0, 'Checkin document - '.$result->message); + $checkoutBy = $result->checked_out_by; + $this->assertTrue(empty($checkoutBy) || $checkoutBy == 'n/a', 'Document should no longer be checked out by anyone - '.$checkoutBy); + + // Download new version + $result = $this->downloadDocument($documentId); + $this->assertEqual($result->status_code, 0, 'Get checkedin document download url - '.$result->message); + $url = $result->message; + + // Download the document + $result = $this->doDownload($url); + $this->assertEqual($result['download'], $this->content_update, 'Document content should be the same as the updated content'); + + // ---------- + + // Get previous versions of the document + $result = $this->getDocumentVersionHistory($documentId); + $this->assertEqual($result->status_code, 0, 'Get document version history - '.$result->message); + $history = $result->history; + $this->assertTrue(is_array($history), 'Version history should be an array'); + $this->assertEqual(count($history), 2, 'Version history should contain 2 items / versions'); + + // Get the previous version number + $version = isset($history[1]) ? $history[1]->content_version : null; + + // Download previous version + $result = $this->downloadDocument($documentId, $version); + $this->assertEqual($result->status_code, 0, "Get document download url for previous version ($version) - ".$result->message); + $url = $result->message; + + // Download the document + $result = $this->doDownload($url); + $this->assertEqual($result['status'], 200, 'Status should be code 200 for success - '.$result['status']); + $this->assertEqual($result['error'], 0, 'Error code should be 0 for success - '.$result['error']); + $this->assertEqual($result['download'], $this->content, 'Document content should be the same as the original content of the initial upload'); + + // ---------- + + // Checkout base64 document + $result = $this->checkoutSmallDocument($documentId2); + $this->assertEqual($result->status_code, 0, 'Checkout base64 document - '.$result->message); + $checkoutBy = $result->checked_out_by; + $checkoutDate = $result->checked_out_date; + $content = $result->message; + $this->assertFalse(is_null($checkoutDate), 'Checked out date should not be null / empty - '.$checkoutDate); + $this->assertEqual($checkoutBy, 'Administrator', 'Using the Administrative user, checked out user must match - '.$checkoutBy); + $this->assertTrue(is_string($content), 'Base64 content should be a string'); + $content = base64_decode($content); + $this->assertEqual($content, $this->content, 'Document content should be the same as that uploaded'); + + // ---------- + + // Checkin a new base64 version + $result = $this->checkinSmallDocument($documentId2, $filename2); + $this->assertEqual($result->status_code, 0, 'Checkin base64 document - '.$result->message); + $checkoutBy = $result->checked_out_by; + $this->assertTrue(empty($checkoutBy) || $checkoutBy == 'n/a', 'Document should no longer be checked out by anyone - '.$checkoutBy); + + // Download new version + $result = $this->downloadSmallDocument($documentId2); + $this->assertEqual($result->status_code, 0, 'Download checkedin base64 document - '.$result->message); + $content = $result->message; + $this->assertTrue(is_string($content), 'Content should be a string'); + $content = base64_decode($content); + $this->assertEqual($content, $this->content_update, 'Document content should be the same as the updated content'); + + // ---------- + + // Get previous versions of the base64 document + $result = $this->getDocumentVersionHistory($documentId2); + $this->assertEqual($result->status_code, 0, 'Get document version history - '.$result->message); + $history = $result->history; + $this->assertTrue(is_array($history), 'Version history should be an array'); + $this->assertEqual(count($history), 2, 'Version history should contain 2 items / versions'); + + // Get the previous version number + $version = isset($history[1]) ? $history[1]->content_version : null; + + // Download previous version + $result = $this->downloadSmallDocument($documentId2, $version); + $this->assertEqual($result->status_code, 0, "Download previous version ($version) - ".$result->message); + $content = $result->message; + $this->assertTrue(is_string($content), 'Content should be a string'); + $content = base64_decode($content); + $this->assertEqual($content, $this->content, 'Document content should be the same as the original content of the initial upload'); + + // delete and cleanup + $this->deleteFolder($folderId); + } + + /** + * Tests getting and updating document metadata + */ + public function testDocumentMetadata() + { + // Create folder containing the documents + $result = $this->createFolder(1, 'Test Metadata Container'); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $folderId = $result->id; + + // Upload document to folder + $filename = 'test_doc.txt'; + $result = $this->createDocument($folderId, 'Test Doc', $filename); + $this->assertEqual($result->status_code, 0, 'Create document - '.$result->message); + $documentId = $result->document_id; + $this->assertTrue(is_numeric($documentId), 'Document id should be numeric'); + + // get document metadata + $result = $this->getDocumentMetadata($documentId); + $this->assertEqual($result->status_code, 0, 'Get document metadata - '.$result->message); + $metadata = $result->metadata; + $this->assertTrue(is_array($metadata), 'Returned document metadata should be an array of fieldsets'); + + // Add a new tag and a document author + foreach ($metadata as $fieldset){ + $fields = $fieldset->fields; + switch($fieldset->fieldset){ + case 'Tag Cloud': + $field_name = 'Tag'; + $curr_val = 'n/a'; + $new_val = 'unit test'; + break; + case 'General information': + $field_name = 'Document Author'; + $curr_val = 'n/a'; + $new_val = 'Test Framework'; + break; + } + + foreach ($fields as $field){ + if($field->name == $field_name){ + $this->assertEqual($field->value, $curr_val, "The current value of the given field, $field_name, should be \"$curr_val\""); + // update the value + $field->value = $new_val; + } + if($field->value == 'n/a'){ + $field->value = ''; + } + } + } + + // update metadata + $result = $this->updateDocumentMetadata($documentId, $metadata); + + // get metadata - ensure it matches the updated metadata + $result = $this->getDocumentMetadata($documentId); + $this->assertEqual($result->status_code, 0, 'Get document metadata - '.$result->message); + $metadata = $result->metadata; + $this->assertTrue(is_array($metadata), 'Returned document metadata should be an array of fieldsets'); + + // Add a new tag and a document author + foreach ($metadata as $fieldset){ + $fields = $fieldset->fields; + switch($fieldset->fieldset){ + case 'Tag Cloud': + $field_name = 'Tag'; + $curr_val = 'unit test'; + break; + case 'General information': + $field_name = 'Document Author'; + $curr_val = 'Test Framework'; + break; + } + + foreach ($fields as $field){ + if($field->name == $field_name){ + $this->assertEqual($field->value, $curr_val, "The current value of the given field, $field_name, should be the same as the updated value: \"$curr_val\""); + } + } + } + + // Get previous versions of the document + $result = $this->getDocumentVersionHistory($documentId); + $this->assertEqual($result->status_code, 0, 'Get document version history - '.$result->message); + $history = $result->history; + $this->assertTrue(is_array($history), 'Version history should be an array'); + $this->assertEqual(count($history), 2, 'Version history should contain 2 items / versions'); + + // Get the previous version number + $version = isset($history[1]) ? $history[1]->metadata_version : null; + + // get document metadata for previous version + $result = $this->getDocumentMetadata($documentId, $version); + $this->assertEqual($result->status_code, 0, 'Get document metadata - '.$result->message); + $metadata = $result->metadata; + $this->assertTrue(is_array($metadata), 'Returned document metadata should be an array of fieldsets'); + + // Add a new tag and a document author + foreach ($metadata as $fieldset){ + $fields = $fieldset->fields; + switch($fieldset->fieldset){ + case 'Tag Cloud': + $field_name = 'Tag'; + $curr_val = 'n/a'; + break; + case 'General information': + $field_name = 'Document Author'; + $curr_val = 'n/a'; + break; + } + + foreach ($fields as $field){ + if($field->name == $field_name){ + $this->assertEqual($field->value, $curr_val, "The current value of the given field, $field_name, should be the same as the previous version's value: \"$curr_val\""); + } + } + } + + // delete and cleanup + $this->deleteFolder($folderId); + } + + /** + * Test getting the contents of a folder + * + */ + public function testGetFolderListing() + { + // create folder + $main_folder = 'Test Listing Container'; + $result = $this->createFolder(1, $main_folder); + $this->assertEqual($result->status_code, 0, 'Create folder - '.$result->message); + $folderId = $result->id; + + // create subfolder 1 + $sub_folder_name = 'Subfolder One'; + $result = $this->createFolder($folderId, $sub_folder_name); + $this->assertEqual($result->status_code, 0, 'Create subfolder 1 - '.$result->message); + $subFolderId1 = $result->id; + + // create subfolder 2 + $result = $this->createFolder($folderId, 'Subfolder Two'); + $this->assertEqual($result->status_code, 0, 'Create subfolder 2 - '.$result->message); + $subFolderId2 = $result->id; + + // create subfolder 3 under subfolder 1 + $result = $this->createFolder($subFolderId1, 'Subfolder Three'); + $this->assertEqual($result->status_code, 0, 'Create subfolder 3 under subfolder 1 - '.$result->message); + $subFolderId3 = $result->id; + + // upload document into main folder + $filename = 'test_doc.txt'; + $result = $this->createDocument($folderId, 'Test Doc', $filename); + $this->assertEqual($result->status_code, 0, 'Create document 1 in folder - '.$result->message); + $documentId = $result->document_id; + $this->assertTrue(is_numeric($documentId), 'Document id should be numeric'); + + // upload document 2 into main folder + $filename2 = 'test_doc2.txt'; + $result = $this->createDocument($folderId, 'Test Doc 2', $filename2); + $this->assertEqual($result->status_code, 0, 'Create document 1 in folder - '.$result->message); + $documentId2 = $result->document_id; + $this->assertTrue(is_numeric($documentId2), 'Document id should be numeric'); + + // upload document 3 into subfolder 1 + $filename3 = 'test_doc3.txt'; + $result = $this->createDocument($subFolderId1, 'Test Doc 3', $filename3); + $this->assertEqual($result->status_code, 0, 'Create document 1 in sub folder 1 - '.$result->message); + $documentId3 = $result->document_id; + $this->assertTrue(is_numeric($documentId3), 'Document id should be numeric'); + + // Get folder listing for folder 1 - folders only, depth of 1 + $result = $this->getFolderContents($folderId, 1, 'F'); + $this->assertEqual($result->status_code, 0, 'Get subfolders in folder - '.$result->message); + $folder_name = $result->folder_name; + $sub_folders = $result->items; + $this->assertEqual($folder_name, $main_folder, 'Folder name is - '.$folder_name); + $this->assertTrue(is_array($sub_folders), 'There should be an array of subfolders'); + $this->assertEqual(count($sub_folders), 2, 'There should be 2 subfolders'); + + // Get folder listing for folder 1 - folders and documents, infinite depth + $result = $this->getFolderContents($folderId, 5, 'FD'); + $this->assertEqual($result->status_code, 0, 'Get all subfolders and documents under the folder - '.$result->message); + $items = $result->items; + $this->assertTrue(is_array($items), 'There should be an array of subfolders and documents'); + $this->assertEqual(count($items), 4, 'There should be 2 subfolders and 2 documents in the immediate folder'); + + // Loop through items, find sub folder 1 + $docs = 0; + $folders = 0; + foreach ($items as $item){ + // increment count of item type + $folders = ($item->item_type == 'F') ? $folders + 1 : $folders; + $docs = ($item->item_type == 'D') ? $docs + 1 : $docs; + + if($item->id == $subFolderId1){ + $sub_items = $item->items; + + $this->assertTrue(is_array($sub_items), 'Subfolder 1 should contain an array of contents'); + $this->assertEqual(count($sub_items), 2, 'Subfolder 1 should contain a folder and document'); + + } + } + + $this->assertEqual($folders, 2, 'There should be 2 folders'); + $this->assertEqual($docs, 2, 'There should be 2 documents'); + + // Get folder listing for subfolder 1 - documents only, depth of 1 + $result = $this->getFolderContents($subFolderId1, 1, 'D'); + $this->assertEqual($result->status_code, 0, 'Get documents under subfolder 1 - '.$result->message); + $folder_name = $result->folder_name; + $items = $result->items; + $this->assertEqual($folder_name, $sub_folder_name, 'Subfolder name is - '.$folder_name); + $this->assertTrue(is_array($items), 'There should be an array of documents'); + $this->assertEqual(count($items), 1, 'There should be 1 document'); + + // delete and cleanup + $this->deleteFolder($folderId); + } +} +?> \ No newline at end of file diff --git a/thirdparty/pear/Cache/Lite.php b/thirdparty/pear/Cache/Lite.php old mode 100644 new mode 100755 index 574870d..574870d --- a/thirdparty/pear/Cache/Lite.php +++ b/thirdparty/pear/Cache/Lite.php diff --git a/thirdparty/pear/Cache/Lite/File.php b/thirdparty/pear/Cache/Lite/File.php old mode 100644 new mode 100755 index 01c2d5b..01c2d5b --- a/thirdparty/pear/Cache/Lite/File.php +++ b/thirdparty/pear/Cache/Lite/File.php diff --git a/thirdparty/pear/Cache/Lite/Function.php b/thirdparty/pear/Cache/Lite/Function.php old mode 100644 new mode 100755 index 020f2cf..020f2cf --- a/thirdparty/pear/Cache/Lite/Function.php +++ b/thirdparty/pear/Cache/Lite/Function.php diff --git a/thirdparty/pear/Cache/Lite/Output.php b/thirdparty/pear/Cache/Lite/Output.php old mode 100644 new mode 100755 index c4e1b76..c4e1b76 --- a/thirdparty/pear/Cache/Lite/Output.php +++ b/thirdparty/pear/Cache/Lite/Output.php diff --git a/thirdparty/pear/Config.php b/thirdparty/pear/Config.php old mode 100644 new mode 100755 index 3f297e1..368f450 --- a/thirdparty/pear/Config.php +++ b/thirdparty/pear/Config.php @@ -15,19 +15,20 @@ // | Author: Bertrand Mansion | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: Config.php,v 1.22 2006/12/22 00:35:34 aashley Exp $ require_once('PEAR.php'); require_once('Config/Container.php'); $GLOBALS['CONFIG_TYPES'] = array( - 'apache' =>array('Config/Container/Apache.php','Config_Container_Apache'), - 'genericconf' =>array('Config/Container/GenericConf.php','Config_Container_GenericConf'), - 'inifile' =>array('Config/Container/IniFile.php','Config_Container_IniFile'), - 'inicommented' =>array('Config/Container/IniCommented.php','Config_Container_IniCommented'), - 'phparray' =>array('Config/Container/PHPArray.php','Config_Container_PHPArray'), - 'xml' =>array('Config/Container/XML.php','Config_Container_XML') + 'apache' => array('Config/Container/Apache.php', 'Config_Container_Apache'), + 'genericconf' => array('Config/Container/GenericConf.php', 'Config_Container_GenericConf'), + 'inifile' => array('Config/Container/IniFile.php', 'Config_Container_IniFile'), + 'inicommented' => array('Config/Container/IniCommented.php', 'Config_Container_IniCommented'), + 'phparray' => array('Config/Container/PHPArray.php', 'Config_Container_PHPArray'), + 'phpconstants' => array('Config/Container/PHPConstants.php', 'Config_Container_PHPConstants'), + 'xml' => array('Config/Container/XML.php', 'Config_Container_XML') ); /** @@ -159,12 +160,12 @@ class Config { function setRoot(&$rootContainer) { if (is_object($rootContainer) && strtolower(get_class($rootContainer)) === 'config_container') { - if ($rootContainer->getName() === 'root' && $rootContainer->getType() === 'section') { - $this->container =& $rootContainer; - } else { - $this->container =& new Config_Container('section', 'root'); - $this->container->addItem($rootContainer); - } + if ($rootContainer->getName() === 'root' && $rootContainer->getType() === 'section') { + $this->container =& $rootContainer; + } else { + $this->container =& new Config_Container('section', 'root'); + $this->container->addItem($rootContainer); + } return true; } else { return PEAR::raiseError("Config::setRoot only accepts object of Config_Container type.", null, PEAR_ERROR_RETURN); @@ -227,4 +228,4 @@ class Config { return $this->container->writeDatasrc($datasrc, $configType, $options); } // end func writeConfig } // end class Config -?> \ No newline at end of file +?> diff --git a/thirdparty/pear/Config/Container.php b/thirdparty/pear/Config/Container.php old mode 100644 new mode 100755 index 5609c86..5609c86 --- a/thirdparty/pear/Config/Container.php +++ b/thirdparty/pear/Config/Container.php diff --git a/thirdparty/pear/Config/Container/Apache.php b/thirdparty/pear/Config/Container/Apache.php old mode 100644 new mode 100755 index 4ac2439..4ac2439 --- a/thirdparty/pear/Config/Container/Apache.php +++ b/thirdparty/pear/Config/Container/Apache.php diff --git a/thirdparty/pear/Config/Container/GenericConf.php b/thirdparty/pear/Config/Container/GenericConf.php old mode 100644 new mode 100755 index f6d5797..f6d5797 --- a/thirdparty/pear/Config/Container/GenericConf.php +++ b/thirdparty/pear/Config/Container/GenericConf.php diff --git a/thirdparty/pear/Config/Container/IniCommented.php b/thirdparty/pear/Config/Container/IniCommented.php old mode 100644 new mode 100755 index f9acf26..f9acf26 --- a/thirdparty/pear/Config/Container/IniCommented.php +++ b/thirdparty/pear/Config/Container/IniCommented.php diff --git a/thirdparty/pear/Config/Container/IniFile.php b/thirdparty/pear/Config/Container/IniFile.php old mode 100644 new mode 100755 index 0086a1d..0086a1d --- a/thirdparty/pear/Config/Container/IniFile.php +++ b/thirdparty/pear/Config/Container/IniFile.php diff --git a/thirdparty/pear/Config/Container/PHPArray.php b/thirdparty/pear/Config/Container/PHPArray.php old mode 100644 new mode 100755 index 9619433..9619433 --- a/thirdparty/pear/Config/Container/PHPArray.php +++ b/thirdparty/pear/Config/Container/PHPArray.php diff --git a/thirdparty/pear/Config/Container/PHPConstants.php b/thirdparty/pear/Config/Container/PHPConstants.php old mode 100644 new mode 100755 index 23914e2..23914e2 --- a/thirdparty/pear/Config/Container/PHPConstants.php +++ b/thirdparty/pear/Config/Container/PHPConstants.php diff --git a/thirdparty/pear/Config/Container/XML.php b/thirdparty/pear/Config/Container/XML.php old mode 100644 new mode 100755 index aea8b16..aea8b16 --- a/thirdparty/pear/Config/Container/XML.php +++ b/thirdparty/pear/Config/Container/XML.php diff --git a/thirdparty/pear/Console/Getopt.php b/thirdparty/pear/Console/Getopt.php old mode 100644 new mode 100755 index 52b2dea..bb9d69c --- a/thirdparty/pear/Console/Getopt.php +++ b/thirdparty/pear/Console/Getopt.php @@ -1,9 +1,9 @@ | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: Getopt.php,v 1.4 2007/06/12 14:58:56 cellog Exp $ require_once 'PEAR.php'; @@ -127,6 +127,10 @@ class Console_Getopt { $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args); if (PEAR::isError($error)) return $error; + } elseif ($arg == '-') { + // - is stdin + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; } else { $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args); if (PEAR::isError($error)) @@ -167,10 +171,14 @@ class Console_Getopt { if ($i + 1 < strlen($arg)) { $opts[] = array($opt, substr($arg, $i + 1)); break; - } else if (list(, $opt_arg) = each($args)) + } else if (list(, $opt_arg) = each($args)) { /* Else use the next argument. */; - else + if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) { + return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt"); + } + } else { return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt"); + } } } @@ -182,26 +190,54 @@ class Console_Getopt { * @access private * */ + function _isShortOpt($arg) + { + return strlen($arg) == 2 && $arg[0] == '-' && preg_match('/[a-zA-Z]/', $arg[1]); + } + + /** + * @access private + * + */ + function _isLongOpt($arg) + { + return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' && + preg_match('/[a-zA-Z]+$/', substr($arg, 2)); + } + + /** + * @access private + * + */ function _parseLongOption($arg, $long_options, &$opts, &$args) { - @list($opt, $opt_arg) = explode('=', $arg); + @list($opt, $opt_arg) = explode('=', $arg, 2); $opt_len = strlen($opt); for ($i = 0; $i < count($long_options); $i++) { $long_opt = $long_options[$i]; $opt_start = substr($long_opt, 0, $opt_len); + $long_opt_name = str_replace('=', '', $long_opt); /* Option doesn't match. Go on to the next one. */ - if ($opt_start != $opt) + if ($long_opt_name != $opt) { continue; + } $opt_rest = substr($long_opt, $opt_len); /* Check that the options uniquely matches one of the allowed options. */ + if ($i + 1 < count($long_options)) { + $next_option_rest = substr($long_options[$i + 1], $opt_len); + } else { + $next_option_rest = ''; + } if ($opt_rest != '' && $opt{0} != '=' && $i + 1 < count($long_options) && - $opt == substr($long_options[$i+1], 0, $opt_len)) { + $opt == substr($long_options[$i+1], 0, $opt_len) && + $next_option_rest != '' && + $next_option_rest{0} != '=') { return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous"); } @@ -212,6 +248,9 @@ class Console_Getopt { if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) { return PEAR::raiseError("Console_Getopt: option --$opt requires an argument"); } + if (Console_Getopt::_isShortOpt($opt_arg) || Console_Getopt::_isLongOpt($opt_arg)) { + return PEAR::raiseError("Console_Getopt: option requires an argument --$opt"); + } } } else if ($opt_arg) { return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument"); diff --git a/thirdparty/pear/DB.php b/thirdparty/pear/DB.php old mode 100644 new mode 100755 index 69477cb..a511979 --- a/thirdparty/pear/DB.php +++ b/thirdparty/pear/DB.php @@ -1,123 +1,260 @@ | -// | Tomas V.V.Cox | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ -// -// Database independent query interface. +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * Database independent query interface + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: DB.php,v 1.88 2007/08/12 05:27:25 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the PEAR class so it can be extended from + */ require_once 'PEAR.php'; + // {{{ constants // {{{ error codes -/* - * The method mapErrorCode in each DB_dbtype implementation maps - * native error codes to one of these. +/**#@+ + * One of PEAR DB's portable error codes. + * @see DB_common::errorCode(), DB::errorMessage() * - * If you add an error code here, make sure you also add a textual - * version of it in DB::errorMessage(). + * {@internal If you add an error code here, make sure you also add a textual + * version of it in DB::errorMessage().}} + */ + +/** + * The code returned by many methods upon success + */ +define('DB_OK', 1); + +/** + * Unkown error + */ +define('DB_ERROR', -1); + +/** + * Syntax error + */ +define('DB_ERROR_SYNTAX', -2); + +/** + * Tried to insert a duplicate value into a primary or unique index + */ +define('DB_ERROR_CONSTRAINT', -3); + +/** + * An identifier in the query refers to a non-existant object + */ +define('DB_ERROR_NOT_FOUND', -4); + +/** + * Tried to create a duplicate object + */ +define('DB_ERROR_ALREADY_EXISTS', -5); + +/** + * The current driver does not support the action you attempted + */ +define('DB_ERROR_UNSUPPORTED', -6); + +/** + * The number of parameters does not match the number of placeholders + */ +define('DB_ERROR_MISMATCH', -7); + +/** + * A literal submitted did not match the data type expected + */ +define('DB_ERROR_INVALID', -8); + +/** + * The current DBMS does not support the action you attempted + */ +define('DB_ERROR_NOT_CAPABLE', -9); + +/** + * A literal submitted was too long so the end of it was removed + */ +define('DB_ERROR_TRUNCATED', -10); + +/** + * A literal number submitted did not match the data type expected + */ +define('DB_ERROR_INVALID_NUMBER', -11); + +/** + * A literal date submitted did not match the data type expected + */ +define('DB_ERROR_INVALID_DATE', -12); + +/** + * Attempt to divide something by zero + */ +define('DB_ERROR_DIVZERO', -13); + +/** + * A database needs to be selected + */ +define('DB_ERROR_NODBSELECTED', -14); + +/** + * Could not create the object requested + */ +define('DB_ERROR_CANNOT_CREATE', -15); + +/** + * Could not drop the database requested because it does not exist + */ +define('DB_ERROR_CANNOT_DROP', -17); + +/** + * An identifier in the query refers to a non-existant table + */ +define('DB_ERROR_NOSUCHTABLE', -18); + +/** + * An identifier in the query refers to a non-existant column + */ +define('DB_ERROR_NOSUCHFIELD', -19); + +/** + * The data submitted to the method was inappropriate + */ +define('DB_ERROR_NEED_MORE_DATA', -20); + +/** + * The attempt to lock the table failed + */ +define('DB_ERROR_NOT_LOCKED', -21); + +/** + * The number of columns doesn't match the number of values */ -define('DB_OK', 1); -define('DB_ERROR', -1); -define('DB_ERROR_SYNTAX', -2); -define('DB_ERROR_CONSTRAINT', -3); -define('DB_ERROR_NOT_FOUND', -4); -define('DB_ERROR_ALREADY_EXISTS', -5); -define('DB_ERROR_UNSUPPORTED', -6); -define('DB_ERROR_MISMATCH', -7); -define('DB_ERROR_INVALID', -8); -define('DB_ERROR_NOT_CAPABLE', -9); -define('DB_ERROR_TRUNCATED', -10); -define('DB_ERROR_INVALID_NUMBER', -11); -define('DB_ERROR_INVALID_DATE', -12); -define('DB_ERROR_DIVZERO', -13); -define('DB_ERROR_NODBSELECTED', -14); -define('DB_ERROR_CANNOT_CREATE', -15); -define('DB_ERROR_CANNOT_DELETE', -16); -define('DB_ERROR_CANNOT_DROP', -17); -define('DB_ERROR_NOSUCHTABLE', -18); -define('DB_ERROR_NOSUCHFIELD', -19); -define('DB_ERROR_NEED_MORE_DATA', -20); -define('DB_ERROR_NOT_LOCKED', -21); define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); -define('DB_ERROR_INVALID_DSN', -23); -define('DB_ERROR_CONNECT_FAILED', -24); + +/** + * The DSN submitted has problems + */ +define('DB_ERROR_INVALID_DSN', -23); + +/** + * Could not connect to the database + */ +define('DB_ERROR_CONNECT_FAILED', -24); + +/** + * The PHP extension needed for this DBMS could not be found + */ define('DB_ERROR_EXTENSION_NOT_FOUND',-25); -define('DB_ERROR_ACCESS_VIOLATION', -26); -define('DB_ERROR_NOSUCHDB', -27); + +/** + * The present user has inadequate permissions to perform the task requestd + */ +define('DB_ERROR_ACCESS_VIOLATION', -26); + +/** + * The database requested does not exist + */ +define('DB_ERROR_NOSUCHDB', -27); + +/** + * Tried to insert a null value into a column that doesn't allow nulls + */ define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); +/**#@-*/ // }}} // {{{ prepared statement-related -/* - * These constants are used when storing information about prepared - * statements (using the "prepare" method in DB_dbtype). +/**#@+ + * Identifiers for the placeholders used in prepared statements. + * @see DB_common::prepare() + */ + +/** + * Indicates a scalar (?) placeholder was used * - * The prepare/execute model in DB is mostly borrowed from the ODBC - * extension, in a query the "?" character means a scalar parameter. - * There are two extensions though, a "&" character means an opaque - * parameter. An opaque parameter is simply a file name, the real - * data are in that file (useful for putting uploaded files into your - * database and such). The "!" char means a parameter that must be - * left as it is. - * They modify the quote behavoir: - * DB_PARAM_SCALAR (?) => 'original string quoted' - * DB_PARAM_OPAQUE (&) => 'string from file quoted' - * DB_PARAM_MISC (!) => original string + * Quote and escape the value as necessary. */ define('DB_PARAM_SCALAR', 1); + +/** + * Indicates an opaque (&) placeholder was used + * + * The value presented is a file name. Extract the contents of that file + * and place them in this column. + */ define('DB_PARAM_OPAQUE', 2); + +/** + * Indicates a misc (!) placeholder was used + * + * The value should not be quoted or escaped. + */ define('DB_PARAM_MISC', 3); +/**#@-*/ // }}} // {{{ binary data-related -/* - * These constants define different ways of returning binary data - * from queries. Again, this model has been borrowed from the ODBC - * extension. - * - * DB_BINMODE_PASSTHRU sends the data directly through to the browser - * when data is fetched from the database. - * DB_BINMODE_RETURN lets you return data as usual. - * DB_BINMODE_CONVERT returns data as well, only it is converted to - * hex format, for example the string "123" would become "313233". +/**#@+ + * The different ways of returning binary data from queries. + */ + +/** + * Sends the fetched data straight through to output */ define('DB_BINMODE_PASSTHRU', 1); -define('DB_BINMODE_RETURN', 2); -define('DB_BINMODE_CONVERT', 3); + +/** + * Lets you return data as usual + */ +define('DB_BINMODE_RETURN', 2); + +/** + * Converts the data to hex format before returning it + * + * For example the string "123" would become "313233". + */ +define('DB_BINMODE_CONVERT', 3); +/**#@-*/ // }}} // {{{ fetch modes +/**#@+ + * Fetch Modes. + * @see DB_common::setFetchMode() + */ + /** - * This is a special constant that tells DB the user hasn't specified - * any particular get mode, so the default should be used. + * Indicates the current default fetch mode should be used + * @see DB_common::$fetchmode */ define('DB_FETCHMODE_DEFAULT', 0); @@ -137,77 +274,99 @@ define('DB_FETCHMODE_ASSOC', 2); define('DB_FETCHMODE_OBJECT', 3); /** - * For multi-dimensional results: normally the first level of arrays - * is the row number, and the second level indexed by column number or name. - * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays - * is the column name, and the second level the row number. + * For multi-dimensional results, make the column name the first level + * of the array and put the row number in the second level of the array + * + * This is flipped from the normal behavior, which puts the row numbers + * in the first level of the array and the column names in the second level. */ define('DB_FETCHMODE_FLIPPED', 4); +/**#@-*/ -/* for compatibility */ +/**#@+ + * Old fetch modes. Left here for compatibility. + */ define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); +/**#@-*/ // }}} // {{{ tableInfo() && autoPrepare()-related -/** - * these are constants for the tableInfo-function - * they are bitwised or'ed. so if there are more constants to be defined - * in the future, adjust DB_TABLEINFO_FULL accordingly +/**#@+ + * The type of information to return from the tableInfo() method. + * + * Bitwised constants, so they can be combined using | + * and removed using ^. + * + * @see DB_common::tableInfo() + * + * {@internal Since the TABLEINFO constants are bitwised, if more of them are + * added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}} */ define('DB_TABLEINFO_ORDER', 1); define('DB_TABLEINFO_ORDERTABLE', 2); define('DB_TABLEINFO_FULL', 3); +/**#@-*/ -/* - * Used by autoPrepare() + +/**#@+ + * The type of query to create with the automatic query building methods. + * @see DB_common::autoPrepare(), DB_common::autoExecute() */ define('DB_AUTOQUERY_INSERT', 1); define('DB_AUTOQUERY_UPDATE', 2); +/**#@-*/ // }}} // {{{ portability modes -/** - * Portability: turn off all portability features. +/**#@+ + * Portability Modes. + * + * Bitwised constants, so they can be combined using | + * and removed using ^. + * * @see DB_common::setOption() + * + * {@internal Since the PORTABILITY constants are bitwised, if more of them are + * added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}} + */ + +/** + * Turn off all portability features */ define('DB_PORTABILITY_NONE', 0); /** - * Portability: convert names of tables and fields to lower case - * when using the get*(), fetch*() and tableInfo() methods. - * @see DB_common::setOption() + * Convert names of tables and fields to lower case + * when using the get*(), fetch*() and tableInfo() methods */ define('DB_PORTABILITY_LOWERCASE', 1); /** - * Portability: right trim the data output by get*() and fetch*(). - * @see DB_common::setOption() + * Right trim the data output by get*() and fetch*() */ define('DB_PORTABILITY_RTRIM', 2); /** - * Portability: force reporting the number of rows deleted. - * @see DB_common::setOption() + * Force reporting the number of rows deleted */ define('DB_PORTABILITY_DELETE_COUNT', 4); /** - * Portability: enable hack that makes numRows() work in Oracle. - * @see DB_common::setOption() + * Enable hack that makes numRows() work in Oracle */ define('DB_PORTABILITY_NUMROWS', 8); /** - * Portability: makes certain error messages in certain drivers compatible - * with those from other DBMS's. + * Makes certain error messages in certain drivers compatible + * with those from other DBMS's * * + mysql, mysqli: change unique/primary key constraints * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT @@ -215,23 +374,20 @@ define('DB_PORTABILITY_NUMROWS', 8); * + odbc(access): MS's ODBC driver reports 'no such field' as code * 07001, which means 'too few parameters.' When this option is on * that code gets mapped to DB_ERROR_NOSUCHFIELD. - * - * @see DB_common::setOption() */ define('DB_PORTABILITY_ERRORS', 16); /** - * Portability: convert null values to empty strings in data output by - * get*() and fetch*(). - * @see DB_common::setOption() + * Convert null values to empty strings in data output by + * get*() and fetch*() */ define('DB_PORTABILITY_NULL_TO_EMPTY', 32); /** - * Portability: turn on all portability features. - * @see DB_common::setOption() + * Turn on all portability features */ define('DB_PORTABILITY_ALL', 63); +/**#@-*/ // }}} @@ -240,12 +396,14 @@ define('DB_PORTABILITY_ALL', 63); // {{{ class DB /** + * Database independent query interface + * * The main "DB" class is simply a container class with some static * methods for creating DB objects as well as some utility functions * common to all parts of DB. * * The object model of DB is as follows (indentation means inheritance): - * + *
  * DB           The main DB class.  This is simply a utility class
  *              with some "static" methods for creating DB objects as
  *              well as common utility functions for other DB classes.
@@ -259,31 +417,32 @@ define('DB_PORTABILITY_ALL', 63);
  *              When calling DB::factory or DB::connect for MySQL
  *              connections, the object returned is an instance of this
  *              class.
+ * 
* - * @package DB - * @author Stig Bakken - * @author Tomas V.V.Cox - * @since PHP 4.0 - * @version $Id$ - * @category Database + * @category Database + * @package DB + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB { // {{{ &factory() /** - * Create a new DB object for the specified database type. - * - * Allows creation of a DB_ object from which the object's - * methods can be utilized without actually connecting to a database. + * Create a new DB object for the specified database type but don't + * connect to the database * - * @param string $type database type, for example "mysql" - * @param array $options associative array of option names and values + * @param string $type the database type (eg "mysql") + * @param array $options an associative array of option names and values * - * @return object a new DB object. On error, an error object. + * @return object a new DB object. A DB_Error object on failure. * * @see DB_common::setOption() - * @access public */ function &factory($type, $options = false) { @@ -302,12 +461,13 @@ class DB if (!class_exists($classname)) { $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, - "Unable to include the DB/{$type}.php file", + "Unable to include the DB/{$type}.php" + . " file for '$dsn'", 'DB_Error', true); return $tmp; } - @$obj =& new $classname; + @$obj = new $classname; foreach ($options as $option => $value) { $test = $obj->setOption($option, $value); @@ -323,35 +483,37 @@ class DB // {{{ &connect() /** - * Create a new DB object and connect to the specified database. + * Create a new DB object including a connection to the specified database * * Example 1. - * * require_once 'DB.php'; * - * $dsn = 'mysql://user:password@host/database' + * $dsn = 'pgsql://user:password@host/database'; * $options = array( * 'debug' => 2, * 'portability' => DB_PORTABILITY_ALL, * ); * - * $dbh =& DB::connect($dsn, $options); - * if (DB::isError($dbh)) { - * die($dbh->getMessage()); + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); * } - * ?> + * * - * @param mixed $dsn string "data source name" or an array in the - * format returned by DB::parseDSN() + * @param mixed $dsn the string "data source name" or array in the + * format returned by DB::parseDSN() + * @param array $options an associative array of option names and values * - * @param array $options an associative array of option names and - * their values + * @return object a new DB object. A DB_Error object on failure. * - * @return object a newly created DB connection object, or a DB - * error object on error + * @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(), + * DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(), + * DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(), + * DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(), + * DB_sybase::connect() * - * @see DB::parseDSN(), DB_common::setOption(), DB::isError() - * @access public + * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError() */ function &connect($dsn, $options = array()) { @@ -376,12 +538,13 @@ class DB $classname = "DB_${type}"; if (!class_exists($classname)) { $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, - "Unable to include the DB/{$type}.php file for `$dsn'", + "Unable to include the DB/{$type}.php" + . " file for '$dsn'", 'DB_Error', true); return $tmp; } - @$obj =& new $classname; + @$obj = new $classname; foreach ($options as $option => $value) { $test = $obj->setOption($option, $value); @@ -392,7 +555,11 @@ class DB $err = $obj->connect($dsninfo, $obj->getOption('persistent')); if (DB::isError($err)) { - $err->addUserInfo($dsn); + if (is_array($dsn)) { + $err->addUserInfo(DB::getDSNString($dsn, true)); + } else { + $err->addUserInfo($dsn); + } return $err; } @@ -405,26 +572,22 @@ class DB /** * Return the DB API version * - * @return int the DB API version number - * - * @access public + * @return string the DB API version number */ function apiVersion() { - return 2; + return '1.7.13'; } // }}} // {{{ isError() /** - * Tell whether a result code from a DB method is an error - * - * @param int $value result code + * Determines if a variable is a DB_Error object * - * @return bool whether $value is an error + * @param mixed $value the variable to check * - * @access public + * @return bool whether $value is DB_Error object */ function isError($value) { @@ -435,13 +598,11 @@ class DB // {{{ isConnection() /** - * Tell whether a value is a DB connection + * Determines if a value is a DB_ object * - * @param mixed $value value to test + * @param mixed $value the value to test * - * @return bool whether $value is a DB connection - * - * @access public + * @return bool whether $value is a DB_ object */ function isConnection($value) { @@ -454,21 +615,24 @@ class DB // {{{ isManip() /** - * Tell whether a query is a data manipulation query (insert, - * update or delete) or a data definition query (create, drop, - * alter, grant, revoke). + * Tell whether a query is a data manipulation or data definition query * - * @access public + * Examples of data manipulation queries are INSERT, UPDATE and DELETE. + * Examples of data definition queries are CREATE, DROP, ALTER, GRANT, + * REVOKE. * - * @param string $query the query + * @param string $query the query * - * @return boolean whether $query is a data manipulation query + * @return boolean whether $query is a data manipulation query */ function isManip($query) { - $manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'. - 'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK'; - if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) { + $manips = 'INSERT|UPDATE|DELETE|REPLACE|' + . 'CREATE|DROP|' + . 'LOAD DATA|SELECT .* INTO .* FROM|COPY|' + . 'ALTER|GRANT|REVOKE|' + . 'LOCK|UNLOCK'; + if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) { return true; } return false; @@ -480,10 +644,10 @@ class DB /** * Return a textual error message for a DB error code * - * @param integer $value error code + * @param integer $value the DB error code * - * @return string error message, or false if the error code was - * not recognized + * @return string the error message or false if the error code was + * not recognized */ function errorMessage($value) { @@ -491,18 +655,23 @@ class DB if (!isset($errorMessages)) { $errorMessages = array( DB_ERROR => 'unknown error', + DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', DB_ERROR_ALREADY_EXISTS => 'already exists', DB_ERROR_CANNOT_CREATE => 'can not create', - DB_ERROR_CANNOT_DELETE => 'can not delete', DB_ERROR_CANNOT_DROP => 'can not drop', + DB_ERROR_CONNECT_FAILED => 'connect failed', DB_ERROR_CONSTRAINT => 'constraint violation', DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', DB_ERROR_DIVZERO => 'division by zero', + DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', DB_ERROR_INVALID => 'invalid', DB_ERROR_INVALID_DATE => 'invalid date or time', + DB_ERROR_INVALID_DSN => 'invalid DSN', DB_ERROR_INVALID_NUMBER => 'invalid number', DB_ERROR_MISMATCH => 'mismatch', + DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', DB_ERROR_NODBSELECTED => 'no database selected', + DB_ERROR_NOSUCHDB => 'no such database', DB_ERROR_NOSUCHFIELD => 'no such field', DB_ERROR_NOSUCHTABLE => 'no such table', DB_ERROR_NOT_CAPABLE => 'DB backend not capable', @@ -510,15 +679,9 @@ class DB DB_ERROR_NOT_LOCKED => 'not locked', DB_ERROR_SYNTAX => 'syntax error', DB_ERROR_UNSUPPORTED => 'not supported', + DB_ERROR_TRUNCATED => 'truncated', DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', - DB_ERROR_INVALID_DSN => 'invalid DSN', - DB_ERROR_CONNECT_FAILED => 'connect failed', DB_OK => 'no error', - DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', - DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', - DB_ERROR_NOSUCHDB => 'no such database', - DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', - DB_ERROR_TRUNCATED => 'truncated' ); } @@ -526,14 +689,15 @@ class DB $value = $value->getCode(); } - return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR]; + return isset($errorMessages[$value]) ? $errorMessages[$value] + : $errorMessages[DB_ERROR]; } // }}} // {{{ parseDSN() /** - * Parse a data source name. + * Parse a data source name * * Additional keys can be added by appending a URI query string to the * end of the DSN. @@ -565,8 +729,6 @@ class DB * + database: Database to use on the DBMS server * + username: User name for login * + password: Password for login - * - * @author Tomas V.V.Cox */ function parseDSN($dsn) { @@ -628,14 +790,14 @@ class DB // Find protocol and hostspec - // $dsn => proto(proto_opts)/database if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { + // $dsn => proto(proto_opts)/database $proto = $match[1]; $proto_opts = $match[2] ? $match[2] : false; $dsn = $match[3]; - // $dsn => protocol+hostspec/database (old format) } else { + // $dsn => protocol+hostspec/database (old format) if (strpos($dsn, '+') !== false) { list($proto, $dsn) = explode('+', $dsn, 2); } @@ -650,12 +812,11 @@ class DB // process the different protocol options $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; $proto_opts = rawurldecode($proto_opts); + if (strpos($proto_opts, ':') !== false) { + list($proto_opts, $parsed['port']) = explode(':', $proto_opts); + } if ($parsed['protocol'] == 'tcp') { - if (strpos($proto_opts, ':') !== false) { - list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts); - } else { - $parsed['hostspec'] = $proto_opts; - } + $parsed['hostspec'] = $proto_opts; } elseif ($parsed['protocol'] == 'unix') { $parsed['socket'] = $proto_opts; } @@ -663,11 +824,11 @@ class DB // Get dabase if any // $dsn => database if ($dsn) { - // /database if (($pos = strpos($dsn, '?')) === false) { + // /database $parsed['database'] = rawurldecode($dsn); - // /database?param1=value1¶m2=value2 } else { + // /database?param1=value1¶m2=value2 $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); $dsn = substr($dsn, $pos + 1); if (strpos($dsn, '&') !== false) { @@ -689,29 +850,81 @@ class DB } // }}} - // {{{ assertExtension() + // {{{ getDSNString() /** - * Load a PHP database extension if it is not loaded already. + * Returns the given DSN in a string format suitable for output. * - * @access public - * - * @param string $name the base name of the extension (without the .so or - * .dll suffix) - * - * @return boolean true if the extension was already or successfully - * loaded, false if it could not be loaded + * @param array|string the DSN to parse and format + * @param boolean true to hide the password, false to include it + * @return string */ - function assertExtension($name) - { - if (!extension_loaded($name)) { - $dlext = OS_WINDOWS ? '.dll' : '.so'; - $dlprefix = OS_WINDOWS ? 'php_' : ''; - @dl($dlprefix . $name . $dlext); - return extension_loaded($name); + function getDSNString($dsn, $hidePassword) { + /* Calling parseDSN will ensure that we have all the array elements + * defined, and means that we deal with strings and array in the same + * manner. */ + $dsnArray = DB::parseDSN($dsn); + + if ($hidePassword) { + $dsnArray['password'] = 'PASSWORD'; } - return true; + + /* Protocol is special-cased, as using the default "tcp" along with an + * Oracle TNS connection string fails. */ + if (is_string($dsn) && strpos($dsn, 'tcp') === false && $dsnArray['protocol'] == 'tcp') { + $dsnArray['protocol'] = false; + } + + // Now we just have to construct the actual string. This is ugly. + $dsnString = $dsnArray['phptype']; + if ($dsnArray['dbsyntax']) { + $dsnString .= '('.$dsnArray['dbsyntax'].')'; + } + $dsnString .= '://' + .$dsnArray['username'] + .':' + .$dsnArray['password'] + .'@' + .$dsnArray['protocol']; + if ($dsnArray['socket']) { + $dsnString .= '('.$dsnArray['socket'].')'; + } + if ($dsnArray['protocol'] && $dsnArray['hostspec']) { + $dsnString .= '+'; + } + $dsnString .= $dsnArray['hostspec']; + if ($dsnArray['port']) { + $dsnString .= ':'.$dsnArray['port']; + } + $dsnString .= '/'.$dsnArray['database']; + + /* Option handling. Unfortunately, parseDSN simply places options into + * the top-level array, so we'll first get rid of the fields defined by + * DB and see what's left. */ + unset($dsnArray['phptype'], + $dsnArray['dbsyntax'], + $dsnArray['username'], + $dsnArray['password'], + $dsnArray['protocol'], + $dsnArray['socket'], + $dsnArray['hostspec'], + $dsnArray['port'], + $dsnArray['database'] + ); + if (count($dsnArray) > 0) { + $dsnString .= '?'; + $i = 0; + foreach ($dsnArray as $key => $value) { + if (++$i > 1) { + $dsnString .= '&'; + } + $dsnString .= $key.'='.$value; + } + } + + return $dsnString; } + // }}} } @@ -720,36 +933,43 @@ class DB /** * DB_Error implements a class for reporting portable database error - * messages. + * messages * - * @package DB - * @author Stig Bakken + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_Error extends PEAR_Error { // {{{ constructor /** - * DB_Error constructor. + * DB_Error constructor * - * @param mixed $code DB error code, or string with error message. - * @param integer $mode what "error mode" to operate in - * @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER - * @param mixed $debuginfo additional debug info, such as the last query - * - * @access public + * @param mixed $code DB error code, or string with error message + * @param int $mode what "error mode" to operate in + * @param int $level what error level to use for $mode & + * PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query * * @see PEAR_Error */ function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, - $level = E_USER_NOTICE, $debuginfo = null) + $level = E_USER_NOTICE, $debuginfo = null) { if (is_int($code)) { - $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo); + $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, + $mode, $level, $debuginfo); } else { - $this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo); + $this->PEAR_Error("DB Error: $code", DB_ERROR, + $mode, $level, $debuginfo); } } + // }}} } @@ -757,62 +977,154 @@ class DB_Error extends PEAR_Error // {{{ class DB_result /** - * This class implements a wrapper for a DB result set. + * This class implements a wrapper for a DB result set + * * A new instance of this class will be returned by the DB implementation * after processing a query that returns data. * - * @package DB - * @author Stig Bakken + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_result { // {{{ properties + /** + * Should results be freed automatically when there are no more rows? + * @var boolean + * @see DB_common::$options + */ + var $autofree; + + /** + * A reference to the DB_ object + * @var object + */ var $dbh; - var $result; - var $row_counter = null; /** - * for limit queries, the row to start fetching + * The current default fetch mode * @var integer + * @see DB_common::$fetchmode */ - var $limit_from = null; + var $fetchmode; /** - * for limit queries, the number of rows to fetch + * The name of the class into which results should be fetched when + * DB_FETCHMODE_OBJECT is in effect + * + * @var string + * @see DB_common::$fetchmode_object_class + */ + var $fetchmode_object_class; + + /** + * The number of rows to fetch from a limit query * @var integer */ var $limit_count = null; + /** + * The row to start fetching from in limit queries + * @var integer + */ + var $limit_from = null; + + /** + * The execute parameters that created this result + * @var array + * @since Property available since Release 1.7.0 + */ + var $parameters; + + /** + * The query string that created this result + * + * Copied here incase it changes in $dbh, which is referenced + * + * @var string + * @since Property available since Release 1.7.0 + */ + var $query; + + /** + * The query result resource id created by PHP + * @var resource + */ + var $result; + + /** + * The present row being dealt with + * @var integer + */ + var $row_counter = null; + + /** + * The prepared statement resource id created by PHP in $dbh + * + * This resource is only available when the result set was created using + * a driver's native execute() method, not PEAR DB's emulated one. + * + * Copied here incase it changes in $dbh, which is referenced + * + * {@internal Mainly here because the InterBase/Firebird API is only + * able to retrieve data from result sets if the statemnt handle is + * still in scope.}} + * + * @var resource + * @since Property available since Release 1.7.0 + */ + var $statement; + + // }}} // {{{ constructor /** - * DB_result constructor. - * @param resource &$dbh DB object reference - * @param resource $result result resource id - * @param array $options assoc array with optional result options + * This constructor sets the object's properties + * + * @param object &$dbh the DB object reference + * @param resource $result the result resource id + * @param array $options an associative array with result options + * + * @return void */ function DB_result(&$dbh, $result, $options = array()) { - $this->dbh = &$dbh; - $this->result = $result; - foreach ($options as $key => $value) { - $this->setOption($key, $value); - } - $this->limit_type = $dbh->features['limit']; $this->autofree = $dbh->options['autofree']; + $this->dbh = &$dbh; $this->fetchmode = $dbh->fetchmode; $this->fetchmode_object_class = $dbh->fetchmode_object_class; + $this->parameters = $dbh->last_parameters; + $this->query = $dbh->last_query; + $this->result = $result; + $this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt; + foreach ($options as $key => $value) { + $this->setOption($key, $value); + } } + /** + * Set options for the DB_result object + * + * @param string $key the option to set + * @param mixed $value the value to set the option to + * + * @return void + */ function setOption($key, $value = null) { switch ($key) { case 'limit_from': - $this->limit_from = $value; break; + $this->limit_from = $value; + break; case 'limit_count': - $this->limit_count = $value; break; + $this->limit_count = $value; } } @@ -820,7 +1132,7 @@ class DB_result // {{{ fetchRow() /** - * Fetch a row of data and return it by reference into an array. + * Fetch a row of data and return it by reference into an array * * The type of array returned can be controlled either by setting this * method's $fetchmode parameter or by changing the default @@ -831,22 +1143,22 @@ class DB_result * DBMS's. These portability options can be turned on when creating a * new DB object or by using setOption(). * - * + DB_PORTABILITY_LOWERCASE + * + DB_PORTABILITY_LOWERCASE * convert names of fields to lower case * - * + DB_PORTABILITY_RTRIM + * + DB_PORTABILITY_RTRIM * right trim the data * - * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $fetchmode the constant indicating how to format the data + * @param int $rownum the row number to fetch (index starts at 0) * - * @return array a row of data, null on no more rows or PEAR_Error - * object on error + * @return mixed an array or object containing the row's data, + * NULL when the end of the result set is reached + * or a DB_Error object on failure. * * @see DB_common::setOption(), DB_common::setFetchMode() - * @access public */ - function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) + function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) { if ($fetchmode === DB_FETCHMODE_DEFAULT) { $fetchmode = $this->fetchmode; @@ -855,19 +1167,18 @@ class DB_result $fetchmode = DB_FETCHMODE_ASSOC; $object_class = $this->fetchmode_object_class; } - if ($this->limit_from !== null) { + if (is_null($rownum) && $this->limit_from !== null) { if ($this->row_counter === null) { $this->row_counter = $this->limit_from; // Skip rows - if ($this->limit_type == false) { + if ($this->dbh->features['limit'] === false) { $i = 0; while ($i++ < $this->limit_from) { $this->dbh->fetchInto($this->result, $arr, $fetchmode); } } } - if ($this->row_counter >= ( - $this->limit_from + $this->limit_count)) + if ($this->row_counter >= ($this->limit_from + $this->limit_count)) { if ($this->autofree) { $this->free(); @@ -875,7 +1186,7 @@ class DB_result $tmp = null; return $tmp; } - if ($this->limit_type == 'emulate') { + if ($this->dbh->features['limit'] === 'emulate') { $rownum = $this->row_counter; } $this->row_counter++; @@ -883,11 +1194,12 @@ class DB_result $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); if ($res === DB_OK) { if (isset($object_class)) { - // default mode specified in DB_common::fetchmode_object_class property + // The default mode is specified in the + // DB_common::fetchmode_object_class property if ($object_class == 'stdClass') { $arr = (object) $arr; } else { - $arr = &new $object_class($arr); + $arr = new $object_class($arr); } } return $arr; @@ -902,7 +1214,7 @@ class DB_result // {{{ fetchInto() /** - * Fetch a row of data into an array which is passed by reference. + * Fetch a row of data into an array which is passed by reference * * The type of array returned can be controlled either by setting this * method's $fetchmode parameter or by changing the default @@ -913,24 +1225,22 @@ class DB_result * DBMS's. These portability options can be turned on when creating a * new DB object or by using setOption(). * - * + DB_PORTABILITY_LOWERCASE + * + DB_PORTABILITY_LOWERCASE * convert names of fields to lower case * - * + DB_PORTABILITY_RTRIM + * + DB_PORTABILITY_RTRIM * right trim the data * - * @param array &$arr (reference) array where data from the row - * should be placed - * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param array &$arr the variable where the data should be placed + * @param int $fetchmode the constant indicating how to format the data + * @param int $rownum the row number to fetch (index starts at 0) * - * @return mixed DB_OK on success, null on no more rows or - * a DB_Error object on error + * @return mixed DB_OK if a row is processed, NULL when the end of the + * result set is reached or a DB_Error object on failure * * @see DB_common::setOption(), DB_common::setFetchMode() - * @access public */ - function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) + function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) { if ($fetchmode === DB_FETCHMODE_DEFAULT) { $fetchmode = $this->fetchmode; @@ -939,11 +1249,11 @@ class DB_result $fetchmode = DB_FETCHMODE_ASSOC; $object_class = $this->fetchmode_object_class; } - if ($this->limit_from !== null) { + if (is_null($rownum) && $this->limit_from !== null) { if ($this->row_counter === null) { $this->row_counter = $this->limit_from; // Skip rows - if ($this->limit_type == false) { + if ($this->dbh->features['limit'] === false) { $i = 0; while ($i++ < $this->limit_from) { $this->dbh->fetchInto($this->result, $arr, $fetchmode); @@ -958,7 +1268,7 @@ class DB_result } return null; } - if ($this->limit_type == 'emulate') { + if ($this->dbh->features['limit'] === 'emulate') { $rownum = $this->row_counter; } @@ -967,7 +1277,8 @@ class DB_result $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); if ($res === DB_OK) { if (isset($object_class)) { - // default mode specified in DB_common::fetchmode_object_class property + // default mode specified in the + // DB_common::fetchmode_object_class property if ($object_class == 'stdClass') { $arr = (object) $arr; } else { @@ -986,11 +1297,9 @@ class DB_result // {{{ numCols() /** - * Get the the number of columns in a result set. + * Get the the number of columns in a result set * - * @return int the number of columns, or a DB error - * - * @access public + * @return int the number of columns. A DB_Error object on failure. */ function numCols() { @@ -1001,26 +1310,63 @@ class DB_result // {{{ numRows() /** - * Get the number of rows in a result set. - * - * @return int the number of rows, or a DB error + * Get the number of rows in a result set * - * @access public + * @return int the number of rows. A DB_Error object on failure. */ function numRows() { - return $this->dbh->numRows($this->result); + if ($this->dbh->features['numrows'] === 'emulate' + && $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS) + { + if ($this->dbh->features['prepare']) { + $res = $this->dbh->query($this->query, $this->parameters); + } else { + $res = $this->dbh->query($this->query); + } + if (DB::isError($res)) { + return $res; + } + $i = 0; + while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) { + $i++; + } + $count = $i; + } else { + $count = $this->dbh->numRows($this->result); + } + + /* fbsql is checked for here because limit queries are implemented + * using a TOP() function, which results in fbsql_num_rows still + * returning the total number of rows that would have been returned, + * rather than the real number. As a result, we'll just do the limit + * calculations for fbsql in the same way as a database with emulated + * limits. Unfortunately, we can't just do this in DB_fbsql::numRows() + * because that only gets the result resource, rather than the full + * DB_Result object. */ + if (($this->dbh->features['limit'] === 'emulate' + && $this->limit_from !== null) + || $this->dbh->phptype == 'fbsql') { + $limit_count = is_null($this->limit_count) ? $count : $this->limit_count; + if ($count < $this->limit_from) { + $count = 0; + } elseif ($count < ($this->limit_from + $limit_count)) { + $count -= $this->limit_from; + } else { + $count = $limit_count; + } + } + + return $count; } // }}} // {{{ nextResult() /** - * Get the next result if a batch of queries was executed. - * - * @return bool true if a new result is available or false if not. + * Get the next result if a batch of queries was executed * - * @access public + * @return bool true if a new result is available or false if not */ function nextResult() { @@ -1031,10 +1377,9 @@ class DB_result // {{{ free() /** - * Frees the resources allocated for this result set. - * @return int error code + * Frees the resources allocated for this result set * - * @access public + * @return bool true on success. A DB_Error object on failure. */ function free() { @@ -1043,6 +1388,7 @@ class DB_result return $err; } $this->result = false; + $this->statement = false; return true; } @@ -1050,9 +1396,8 @@ class DB_result // {{{ tableInfo() /** - * @deprecated - * @internal * @see DB_common::tableInfo() + * @deprecated Method deprecated some time before Release 1.2 */ function tableInfo($mode = null) { @@ -1063,16 +1408,33 @@ class DB_result } // }}} + // {{{ getQuery() + + /** + * Determine the query string that created this result + * + * @return string the query string + * + * @since Method available since Release 1.7.0 + */ + function getQuery() + { + return $this->query; + } + + // }}} // {{{ getRowCounter() /** - * returns the actual row number - * @return integer + * Tells which row number is currently being processed + * + * @return integer the current row being looked at. Starts at 1. */ function getRowCounter() { return $this->row_counter; } + // }}} } @@ -1080,17 +1442,30 @@ class DB_result // {{{ class DB_row /** - * Pear DB Row Object - * @see DB_common::setFetchMode() + * PEAR DB Row Object + * + * The object contains a row of data from a result set. Each column's data + * is placed in a property named for the column. + * + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB + * @see DB_common::setFetchMode() */ class DB_row { // {{{ constructor /** - * constructor + * The constructor places a row's data into properties of this object + * + * @param array the array containing the row's data * - * @param resource row data as array + * @return void */ function DB_row(&$arr) { diff --git a/thirdparty/pear/DB/common.php b/thirdparty/pear/DB/common.php old mode 100644 new mode 100755 index c33d337..8d40312 --- a/thirdparty/pear/DB/common.php +++ b/thirdparty/pear/DB/common.php @@ -1,154 +1,248 @@ | -// | Tomas V.V.Cox | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * Contains the DB_common base class + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: common.php,v 1.143 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the PEAR class so it can be extended from + */ require_once 'PEAR.php'; /** - * DB_common is a base class for DB implementations, and must be - * inherited by all such + * DB_common is the base class from which each database driver class extends + * + * All common methods are declared here. If a given DBMS driver contains + * a particular method, that method will overload the one here. * - * @package DB - * @version $Id$ - * @category Database - * @author Stig Bakken - * @author Tomas V.V.Cox + * @category Database + * @package DB + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_common extends PEAR { // {{{ properties /** - * assoc of capabilities for this DB implementation - * $features['limit'] => 'emulate' => emulate with fetch row by number - * 'alter' => alter the query - * false => skip rows - * @var array + * The current default fetch mode + * @var integer */ - var $features = array(); + var $fetchmode = DB_FETCHMODE_ORDERED; /** - * assoc mapping native error codes to DB ones - * @var array + * The name of the class into which results should be fetched when + * DB_FETCHMODE_OBJECT is in effect + * + * @var string */ - var $errorcode_map = array(); + var $fetchmode_object_class = 'stdClass'; /** - * DB type (mysql, oci8, odbc etc.) - * @var string + * Was a connection present when the object was serialized()? + * @var bool + * @see DB_common::__sleep(), DB_common::__wake() */ - var $phptype; + var $was_connected = null; /** + * The most recently executed query * @var string */ - var $prepare_tokens; + var $last_query = ''; /** - * @var string + * Run-time configuration options + * + * The 'optimize' option has been deprecated. Use the 'portability' + * option instead. + * + * @var array + * @see DB_common::setOption() */ - var $prepare_types; + var $options = array( + 'result_buffering' => 500, + 'persistent' => false, + 'ssl' => false, + 'debug' => 0, + 'seqname_format' => '%s_seq', + 'autofree' => false, + 'portability' => DB_PORTABILITY_NONE, + 'optimize' => 'performance', // Deprecated. Use 'portability'. + ); /** - * @var string + * The parameters from the most recently executed query + * @var array + * @since Property available since Release 1.7.0 */ - var $prepared_queries; + var $last_parameters = array(); /** - * @var integer + * The elements from each prepared statement + * @var array */ - var $prepare_maxstmt = 0; + var $prepare_tokens = array(); /** - * @var string + * The data types of the various elements in each prepared statement + * @var array */ - var $last_query = ''; + var $prepare_types = array(); /** - * @var integer + * The prepared queries + * @var array */ - var $fetchmode = DB_FETCHMODE_ORDERED; + var $prepared_queries = array(); /** - * @var string + * Flag indicating that the last query was a manipulation query. + * @access protected + * @var boolean */ - var $fetchmode_object_class = 'stdClass'; + var $_last_query_manip = false; /** - * Run-time configuration options. + * Flag indicating that the next query must be a manipulation + * query. + * @access protected + * @var boolean + */ + var $_next_query_manip = false; + + + // }}} + // {{{ DB_common + + /** + * This constructor calls $this->PEAR('DB_Error') * - * The 'optimize' option has been deprecated. Use the 'portability' - * option instead. + * @return void + */ + function DB_common() + { + $this->PEAR('DB_Error'); + } + + // }}} + // {{{ __sleep() + + /** + * Automatically indicates which properties should be saved + * when PHP's serialize() function is called * - * @see DB_common::setOption() - * @var array + * @return array the array of properties names that should be saved */ - var $options = array( - 'persistent' => false, - 'ssl' => false, - 'debug' => 0, - 'seqname_format' => '%s_seq', - 'autofree' => false, - 'portability' => DB_PORTABILITY_NONE, - 'optimize' => 'performance', // Deprecated. Use 'portability'. - ); + function __sleep() + { + if ($this->connection) { + // Don't disconnect(), people use serialize() for many reasons + $this->was_connected = true; + } else { + $this->was_connected = false; + } + if (isset($this->autocommit)) { + return array('autocommit', + 'dbsyntax', + 'dsn', + 'features', + 'fetchmode', + 'fetchmode_object_class', + 'options', + 'was_connected', + ); + } else { + return array('dbsyntax', + 'dsn', + 'features', + 'fetchmode', + 'fetchmode_object_class', + 'options', + 'was_connected', + ); + } + } + + // }}} + // {{{ __wakeup() /** - * DB handle - * @var resource + * Automatically reconnects to the database when PHP's unserialize() + * function is called + * + * The reconnection attempt is only performed if the object was connected + * at the time PHP's serialize() function was run. + * + * @return void */ - var $dbh; + function __wakeup() + { + if ($this->was_connected) { + $this->connect($this->dsn, $this->options); + } + } // }}} - // {{{ toString() + // {{{ __toString() /** - * String conversation + * Automatic string conversion for PHP 5 + * + * @return string a string describing the current PEAR DB object * - * @return string - * @access private + * @since Method available since Release 1.7.0 */ - function toString() + function __toString() { $info = strtolower(get_class($this)); $info .= ': (phptype=' . $this->phptype . ', dbsyntax=' . $this->dbsyntax . ')'; - if ($this->connection) { $info .= ' [connected]'; } - return $info; } // }}} - // {{{ constructor + // {{{ toString() /** - * Constructor + * DEPRECATED: String conversion method + * + * @return string a string describing the current PEAR DB object + * + * @deprecated Method deprecated in Release 1.7.0 */ - function DB_common() + function toString() { - $this->PEAR('DB_Error'); + return $this->__toString(); } // }}} @@ -158,11 +252,12 @@ class DB_common extends PEAR * DEPRECATED: Quotes a string so it can be safely used within string * delimiters in a query * - * @return string quoted string + * @param string $string the string to be quoted + * + * @return string the quoted string * * @see DB_common::quoteSmart(), DB_common::escapeSimple() - * @deprecated Deprecated in release 1.2 or lower - * @internal + * @deprecated Method deprecated some time before Release 1.2 */ function quoteString($string) { @@ -179,25 +274,25 @@ class DB_common extends PEAR /** * DEPRECATED: Quotes a string so it can be safely used in a query * - * @param string $string the input string to quote + * @param string $string the string to quote * - * @return string The NULL string or the string quotes - * in magic_quote_sybase style + * @return string the quoted string or the string NULL + * if the value submitted is null. * * @see DB_common::quoteSmart(), DB_common::escapeSimple() - * @deprecated Deprecated in release 1.6.0 - * @internal + * @deprecated Deprecated in release 1.6.0 */ function quote($string = null) { - return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'"; + return ($string === null) ? 'NULL' + : "'" . str_replace("'", "''", $string) . "'"; } // }}} // {{{ quoteIdentifier() /** - * Quote a string so it can be safely used as a table or column name + * Quotes a string so it can be safely used as a table or column name * * Delimiting style depends on which database driver is being used. * @@ -221,17 +316,17 @@ class DB_common extends PEAR * + odbc(db2) * + pgsql * + sqlite - * + sybase + * + sybase (must execute set quoted_identifier on sometime + * prior to use) * * InterBase doesn't seem to be able to use delimited identifiers * via PHP 4. They work fine under PHP 5. * - * @param string $str identifier name to be quoted + * @param string $str the identifier name to be quoted * - * @return string quoted identifier string + * @return string the quoted identifier * - * @since 1.6.0 - * @access public + * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { @@ -242,16 +337,15 @@ class DB_common extends PEAR // {{{ quoteSmart() /** - * Format input so it can be safely used in a query + * Formats input so it can be safely used in a query * * The output depends on the PHP data type of input and the database * type being used. * - * @param mixed $in data to be quoted - * - * @return mixed the format of the results depends on the input's - * PHP type: + * @param mixed $in the data to be formatted * + * @return mixed the formatted data. The format depends on the input's + * PHP type: *
    *
  • * input -> returns @@ -263,7 +357,7 @@ class DB_common extends PEAR * integer or double -> the unquoted number *
  • *
  • - * &type.bool; -> output depends on the driver in use + * bool -> output depends on the driver in use * Most drivers return integers: 1 if * true or 0 if * false. @@ -340,192 +434,116 @@ class DB_common extends PEAR *
  • *
* - * @since 1.6.0 * @see DB_common::escapeSimple() - * @access public + * @since Method available since Release 1.6.0 */ function quoteSmart($in) { - if (is_int($in) || is_double($in)) { + if (is_int($in)) { return $in; + } elseif (is_float($in)) { + return $this->quoteFloat($in); } elseif (is_bool($in)) { - return $in ? 1 : 0; + return $this->quoteBoolean($in); } elseif (is_null($in)) { return 'NULL'; } else { + if ($this->dbsyntax == 'access' + && preg_match('/^#.+#$/', $in)) + { + return $this->escapeSimple($in); + } return "'" . $this->escapeSimple($in) . "'"; } } // }}} - // {{{ escapeSimple() + // {{{ quoteBoolean() /** - * Escape a string according to the current DBMS's standards - * - * In SQLite, this makes things safe for inserts/updates, but may - * cause problems when performing text comparisons against columns - * containing binary data. See the - * {@link http://php.net/sqlite_escape_string PHP manual} for more info. - * - * @param string $str the string to be escaped - * - * @return string the escaped string + * Formats a boolean value for use within a query in a locale-independent + * manner. * - * @since 1.6.0 + * @param boolean the boolean value to be quoted. + * @return string the quoted string. * @see DB_common::quoteSmart() - * @access public + * @since Method available since release 1.7.8. */ - function escapeSimple($str) { - return str_replace("'", "''", $str); + function quoteBoolean($boolean) { + return $boolean ? '1' : '0'; } - + // }}} - // {{{ provides() + // {{{ quoteFloat() /** - * Tell whether a DB implementation or its backend extension - * supports a given feature + * Formats a float value for use within a query in a locale-independent + * manner. * - * @param array $feature name of the feature (see the DB class doc) - * @return bool whether this DB implementation supports $feature - * @access public + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. */ - function provides($feature) - { - return $this->features[$feature]; + function quoteFloat($float) { + return "'".$this->escapeSimple(str_replace(',', '.', strval(floatval($float))))."'"; } - + // }}} - // {{{ errorCode() + // {{{ escapeSimple() /** - * Map native error codes to DB's portable ones + * Escapes a string according to the current DBMS's standards * - * Requires that the DB implementation's constructor fills - * in the $errorcode_map property. - * - * @param mixed $nativecode the native error code, as returned by the - * backend database extension (string or integer) - * - * @return int a portable DB error code, or DB_ERROR if this DB - * implementation has no mapping for the given error code. - * - * @access public - */ - function errorCode($nativecode) - { - if (isset($this->errorcode_map[$nativecode])) { - return $this->errorcode_map[$nativecode]; - } - // Fall back to DB_ERROR if there was no mapping. - return DB_ERROR; - } - - // }}} - // {{{ errorMessage() - - /** - * Map a DB error code to a textual message. This is actually - * just a wrapper for DB::errorMessage() + * In SQLite, this makes things safe for inserts/updates, but may + * cause problems when performing text comparisons against columns + * containing binary data. See the + * {@link http://php.net/sqlite_escape_string PHP manual} for more info. * - * @param integer $dbcode the DB error code + * @param string $str the string to be escaped * - * @return string the corresponding error message, of false - * if the error code was unknown + * @return string the escaped string * - * @access public + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 */ - function errorMessage($dbcode) + function escapeSimple($str) { - return DB::errorMessage($this->errorcode_map[$dbcode]); + return str_replace("'", "''", $str); } // }}} - // {{{ raiseError() + // {{{ provides() /** - * Communicate an error and invoke error callbacks, etc - * - * Basically a wrapper for PEAR::raiseError without the message string. - * - * @param mixed integer error code, or a PEAR error object (all - * other parameters are ignored if this parameter is - * an object - * - * @param int error mode, see PEAR_Error docs - * - * @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the - * error level (E_USER_NOTICE etc). If error mode is - * PEAR_ERROR_CALLBACK, this is the callback function, - * either as a function name, or as an array of an - * object and method name. For other error modes this - * parameter is ignored. - * - * @param string Extra debug information. Defaults to the last - * query and native error code. + * Tells whether the present driver supports a given feature * - * @param mixed Native error code, integer or string depending the - * backend. + * @param string $feature the feature you're curious about * - * @return object a PEAR error object - * - * @access public - * @see PEAR_Error + * @return bool whether this driver supports $feature */ - function &raiseError($code = DB_ERROR, $mode = null, $options = null, - $userinfo = null, $nativecode = null) + function provides($feature) { - // The error is yet a DB error object - if (is_object($code)) { - // because we the static PEAR::raiseError, our global - // handler should be used if it is set - if ($mode === null && !empty($this->_default_error_mode)) { - $mode = $this->_default_error_mode; - $options = $this->_default_error_options; - } - $tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true); - return $tmp; - } - - if ($userinfo === null) { - $userinfo = $this->last_query; - } - - if ($nativecode) { - $userinfo .= ' [nativecode=' . trim($nativecode) . ']'; - } - - $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, - 'DB_Error', true); - return $tmp; + return $this->features[$feature]; } // }}} // {{{ setFetchMode() /** - * Sets which fetch mode should be used by default on queries - * on this connection - * - * @param integer $fetchmode DB_FETCHMODE_ORDERED or - * DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with - * DB_FETCHMODE_FLIPPED. - * - * @param string $object_class The class of the object - * to be returned by the fetch methods when - * the DB_FETCHMODE_OBJECT mode is selected. - * If no class is specified by default a cast - * to object from the assoc array row will be done. - * There is also the posibility to use and extend the - * 'DB_row' class. - * - * @see DB_FETCHMODE_ORDERED - * @see DB_FETCHMODE_ASSOC - * @see DB_FETCHMODE_FLIPPED - * @see DB_FETCHMODE_OBJECT - * @see DB_row::DB_row() - * @access public + * Sets the fetch mode that should be used by default for query results + * + * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC + * or DB_FETCHMODE_OBJECT + * @param string $object_class the class name of the object to be returned + * by the fetch methods when the + * DB_FETCHMODE_OBJECT mode is selected. + * If no class is specified by default a cast + * to object from the assoc array row will be + * done. There is also the posibility to use + * and extend the 'DB_row' class. + * + * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT */ function setFetchMode($fetchmode, $object_class = 'stdClass') { @@ -545,7 +563,7 @@ class DB_common extends PEAR // {{{ setOption() /** - * Set run-time configuration options for PEAR DB + * Sets run-time configuration options for PEAR DB * * Options, their data types, default values and description: *
    @@ -554,6 +572,13 @@ class DB_common extends PEAR *
    should results be freed automatically when there are no * more rows? *
  • + * result_buffering integer = 500 + *
    how many rows of the result set should be buffered? + *
    In mysql: mysql_unbuffered_query() is used instead of + * mysql_query() if this value is 0. (Release 1.7.0) + *
    In oci8: this value is passed to ocisetprefetch(). + * (Release 1.7.0) + *
  • * debug integer = 0 *
    debug level *
  • @@ -640,7 +665,6 @@ class DB_common extends PEAR * that code gets mapped to DB_ERROR_NOSUCHFIELD. * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD * - * * DB_PORTABILITY_NULL_TO_EMPTY * convert null values to empty strings in data output by get*() and * fetch*(). Needed because Oracle considers empty strings to be null, @@ -653,26 +677,26 @@ class DB_common extends PEAR * ----------------------------------------- * * Example 1. Simple setOption() example - * setOption('autofree', true); - * ?> + * + * $db->setOption('autofree', true); + * * * Example 2. Portability for lowercasing and trimming - * setOption('portability', - * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); - * ?> + * + * $db->setOption('portability', + * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); + * * * Example 3. All portability options except trimming - * setOption('portability', - * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); - * ?> + * + * $db->setOption('portability', + * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); + * * * @param string $option option name * @param mixed $value value for the option * - * @return int DB_OK on success. DB_Error object on failure. + * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::$options */ @@ -717,9 +741,9 @@ class DB_common extends PEAR /** * Returns the value of an option * - * @param string $option option name + * @param string $option the option name you're curious about * - * @return mixed the option value + * @return mixed the option's value */ function getOption($option) { @@ -748,15 +772,15 @@ class DB_common extends PEAR * data in a db) * * Example 1. - * prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); + * + * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); * $data = array( * "John's text", * "'it''s good'", * 'filename.txt' * ); - * $res = $dbh->execute($sth, $data); - * ?> + * $res = $db->execute($sth, $data); + * * * Use backslashes to escape placeholder characters if you don't want * them to be interpreted as placeholders: @@ -768,12 +792,12 @@ class DB_common extends PEAR * * {@internal ibase and oci8 have their own prepare() methods.}} * - * @param string $query query to be prepared + * @param string $query the query to be prepared * - * @return mixed DB statement resource on success. DB_Error on failure. + * @return mixed DB statement resource on success. A DB_Error object + * on failure. * * @see DB_common::execute() - * @access public */ function prepare($query) { @@ -813,19 +837,27 @@ class DB_common extends PEAR // {{{ autoPrepare() /** - * Automaticaly generate an insert or update query and pass it to prepare() + * Automaticaly generates an insert or update query and pass it to prepare() * - * @param string $table name of the table - * @param array $table_fields ordered array containing the fields names - * @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) - * @param string $where in case of update queries, this string will be put after the sql WHERE statement - * @return resource handle for the query - * @see DB_common::prepare(), DB_common::buildManipSQL() - * @access public + * @param string $table the table name + * @param array $table_fields the array of field names + * @param int $mode a type of query to make: + * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE + * @param string $where for update queries: the WHERE clause to + * append to the SQL statement. Don't + * include the "WHERE" keyword. + * + * @return resource the query handle + * + * @uses DB_common::prepare(), DB_common::buildManipSQL() */ - function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false) + function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, + $where = false) { $query = $this->buildManipSQL($table, $table_fields, $mode, $where); + if (DB::isError($query)) { + return $query; + } return $this->prepare($query); } @@ -833,21 +865,33 @@ class DB_common extends PEAR // {{{ autoExecute() /** - * Automaticaly generate an insert or update query and call prepare() + * Automaticaly generates an insert or update query and call prepare() * and execute() with it * - * @param string $table name of the table - * @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value - * @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) - * @param string $where in case of update queries, this string will be put after the sql WHERE statement - * @return mixed a new DB_Result or a DB_Error when fail - * @see DB_common::autoPrepare(), DB_common::buildManipSQL() - * @access public + * @param string $table the table name + * @param array $fields_values the associative array where $key is a + * field name and $value its value + * @param int $mode a type of query to make: + * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE + * @param string $where for update queries: the WHERE clause to + * append to the SQL statement. Don't + * include the "WHERE" keyword. + * + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. + * + * @uses DB_common::autoPrepare(), DB_common::execute() */ - function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false) + function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, + $where = false) { - $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where); - $ret =& $this->execute($sth, array_values($fields_values)); + $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, + $where); + if (DB::isError($sth)) { + return $sth; + } + $ret = $this->execute($sth, array_values($fields_values)); $this->freePrepared($sth); return $ret; @@ -857,25 +901,39 @@ class DB_common extends PEAR // {{{ buildManipSQL() /** - * Make automaticaly an sql query for prepare() + * Produces an SQL query string for autoPrepare() * - * Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), DB_AUTOQUERY_INSERT) - * will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) - * NB : - This belongs more to a SQL Builder class, but this is a simple facility - * - Be carefull ! If you don't give a $where param with an UPDATE query, all - * the records of the table will be updated ! + * Example: + *
    +     * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
    +     *               DB_AUTOQUERY_INSERT);
    +     * 
    * - * @param string $table name of the table - * @param array $table_fields ordered array containing the fields names - * @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE) - * @param string $where in case of update queries, this string will be put after the sql WHERE statement - * @return string sql query for prepare() - * @access public + * That returns + * + * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) + * + * + * NOTES: + * - This belongs more to a SQL Builder class, but this is a simple + * facility. + * - Be carefull! If you don't give a $where param with an UPDATE + * query, all the records of the table will be updated! + * + * @param string $table the table name + * @param array $table_fields the array of field names + * @param int $mode a type of query to make: + * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE + * @param string $where for update queries: the WHERE clause to + * append to the SQL statement. Don't + * include the "WHERE" keyword. + * + * @return string the sql query for autoPrepare() */ function buildManipSQL($table, $table_fields, $mode, $where = false) { if (count($table_fields) == 0) { - $this->raiseError(DB_ERROR_NEED_MORE_DATA); + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); } $first = true; switch ($mode) { @@ -909,7 +967,7 @@ class DB_common extends PEAR } return $sql; default: - $this->raiseError(DB_ERROR_SYNTAX); + return $this->raiseError(DB_ERROR_SYNTAX); } } @@ -920,29 +978,30 @@ class DB_common extends PEAR * Executes a DB statement prepared with prepare() * * Example 1. - * prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); + * + * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); * $data = array( * "John's text", * "'it''s good'", * 'filename.txt' * ); - * $res =& $dbh->execute($sth, $data); - * ?> + * $res = $db->execute($sth, $data); + * * - * @param resource $stmt a DB statement resource returned from prepare() - * @param mixed $data array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @return object a new DB_Result or a DB_Error when fail + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. * * {@internal ibase and oci8 have their own execute() methods.}} * * @see DB_common::prepare() - * @access public */ function &execute($stmt, $data = array()) { @@ -952,10 +1011,10 @@ class DB_common extends PEAR } $result = $this->simpleQuery($realquery); - if (DB::isError($result) || $result === DB_OK) { + if ($result === DB_OK || DB::isError($result)) { return $result; } else { - $tmp =& new DB_result($this, $result); + $tmp = new DB_result($this, $result); return $tmp; } } @@ -964,27 +1023,26 @@ class DB_common extends PEAR // {{{ executeEmulateQuery() /** - * Emulates the execute statement, when not supported + * Emulates executing prepared statements if the DBMS not support them * - * @param resource $stmt a DB statement resource returned from execute() - * @param mixed $data array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. + * @param resource $stmt a DB statement resource returned from execute() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @return mixed a string containing the real query run when emulating - * prepare/execute. A DB error code is returned on failure. + * @return mixed a string containing the real query run when emulating + * prepare/execute. A DB_Error object on failure. * + * @access protected * @see DB_common::execute() - * @access private */ function executeEmulateQuery($stmt, $data = array()) { $stmt = (int)$stmt; - if (!is_array($data)) { - $data = array($data); - } + $data = (array)$data; + $this->last_parameters = $data; if (count($this->prepare_types[$stmt]) != count($data)) { $this->last_query = $this->prepared_queries[$stmt]; @@ -1018,8 +1076,7 @@ class DB_common extends PEAR // {{{ executeMultiple() /** - * This function does several execute() calls on the same - * statement handle + * Performs several execute() calls on the same statement handle * * $data must be an array indexed numerically * from 0, one execute call is done for every "row" in the array. @@ -1027,19 +1084,18 @@ class DB_common extends PEAR * If an error occurs during execute(), executeMultiple() does not * execute the unfinished rows, but rather returns that error. * - * @param resource $stmt query handle from prepare() - * @param array $data numeric array containing the - * data to insert into the query + * @param resource $stmt query handle from prepare() + * @param array $data numeric array containing the + * data to insert into the query * - * @return mixed DB_OK or DB_Error + * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::prepare(), DB_common::execute() - * @access public */ function executeMultiple($stmt, $data) { foreach ($data as $value) { - $res =& $this->execute($stmt, $value); + $res = $this->execute($stmt, $value); if (DB::isError($res)) { return $res; } @@ -1051,15 +1107,20 @@ class DB_common extends PEAR // {{{ freePrepared() /** - * Free the resource used in a prepared query + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's PHP resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. + * + * @return bool TRUE on success, FALSE if $result is invalid * - * @param $stmt The resurce returned by the prepare() function * @see DB_common::prepare() */ - function freePrepared($stmt) + function freePrepared($stmt, $free_resource = true) { $stmt = (int)$stmt; - // Free the internal prepared vars if (isset($this->prepare_tokens[$stmt])) { unset($this->prepare_tokens[$stmt]); unset($this->prepare_types[$stmt]); @@ -1073,19 +1134,20 @@ class DB_common extends PEAR // {{{ modifyQuery() /** - * This method is used by backends to alter queries for various - * reasons + * Changes a query string for various DBMS specific reasons * - * It is defined here to assure that all implementations - * have this method defined. + * It is defined here to ensure all drivers have this method available. * - * @param string $query query to modify + * @param string $query the query string to modify * - * @return the new (modified) query + * @return string the modified query string * - * @access private + * @access protected + * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(), + * DB_sqlite::modifyQuery() */ - function modifyQuery($query) { + function modifyQuery($query) + { return $query; } @@ -1093,15 +1155,23 @@ class DB_common extends PEAR // {{{ modifyLimitQuery() /** - * This method is used by backends to alter limited queries + * Adds LIMIT clauses to a query string according to current DBMS standards * - * @param string $query query to modify - * @param integer $from the row to start to fetching - * @param integer $count the numbers of rows to fetch + * It is defined here to assure that all implementations + * have this method defined. * - * @return the new (modified) query + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @access private + * @return string the query string with LIMIT clauses added + * + * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { @@ -1112,25 +1182,24 @@ class DB_common extends PEAR // {{{ query() /** - * Send a query to the database and return any results with a - * DB_result object + * Sends a query to the database server * * The query string can be either a normal statement to be sent directly * to the server OR if $params are passed the query can have * placeholders and it will be passed through prepare() and execute(). * - * @param string $query the SQL query or the statement to prepare - * @param mixed $params array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. + * @param string $query the SQL query or the statement to prepare + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @return mixed a DB_result object or DB_OK on success, a DB - * error on failure + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. * * @see DB_result, DB_common::prepare(), DB_common::execute() - * @access public */ function &query($query, $params = array()) { @@ -1139,15 +1208,16 @@ class DB_common extends PEAR if (DB::isError($sth)) { return $sth; } - $ret =& $this->execute($sth, $params); - $this->freePrepared($sth); + $ret = $this->execute($sth, $params); + $this->freePrepared($sth, false); return $ret; } else { + $this->last_parameters = array(); $result = $this->simpleQuery($query); - if (DB::isError($result) || $result === DB_OK) { + if ($result === DB_OK || DB::isError($result)) { return $result; } else { - $tmp =& new DB_result($this, $result); + $tmp = new DB_result($this, $result); return $tmp; } } @@ -1157,20 +1227,20 @@ class DB_common extends PEAR // {{{ limitQuery() /** - * Generates a limited query - * - * @param string $query query - * @param integer $from the row to start to fetching - * @param integer $count the numbers of rows to fetch - * @param mixed $params array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. - * - * @return mixed a DB_Result object, DB_OK or a DB_Error - * - * @access public + * Generates and executes a LIMIT query + * + * @param string $query the query + * @param intr $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. */ function &limitQuery($query, $from, $count, $params = array()) { @@ -1178,7 +1248,7 @@ class DB_common extends PEAR if (DB::isError($query)){ return $query; } - $result =& $this->query($query, $params); + $result = $this->query($query, $params); if (is_a($result, 'DB_result')) { $result->setOption('limit_from', $from); $result->setOption('limit_count', $count); @@ -1190,34 +1260,33 @@ class DB_common extends PEAR // {{{ getOne() /** - * Fetch the first column of the first row of data returned from - * a query + * Fetches the first column of the first row from a query result * * Takes care of doing the query and freeing the results when finished. * - * @param string $query the SQL query - * @param mixed $params array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. - * - * @return mixed the returned value of the query. DB_Error on failure. + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @access public + * @return mixed the returned value of the query. + * A DB_Error object on failure. */ function &getOne($query, $params = array()) { - settype($params, 'array'); + $params = (array)$params; + // modifyLimitQuery() would be nice here, but it causes BC issues if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } - $res =& $this->execute($sth, $params); + $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { - $res =& $this->query($query); + $res = $this->query($query); } if (DB::isError($res)) { @@ -1238,24 +1307,22 @@ class DB_common extends PEAR // {{{ getRow() /** - * Fetch the first row of data returned from a query + * Fetches the first row of data returned from a query result * * Takes care of doing the query and freeing the results when finished. * - * @param string $query the SQL query - * @param array $params array to be used in execution of the statement. - * Quantity of array elements must match quantity - * of placeholders in query. This function does - * NOT support scalars. - * @param int $fetchmode the fetch mode to use - * - * @return array the first row of results as an array indexed from - * 0, or a DB error code. + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use * - * @access public + * @return array the first row of results as an array. + * A DB_Error object on failure. */ - function &getRow($query, - $params = array(), + function &getRow($query, $params = array(), $fetchmode = DB_FETCHMODE_DEFAULT) { // compat check, the params and fetchmode parameters used to @@ -1274,16 +1341,16 @@ class DB_common extends PEAR $params = array(); } } - + // modifyLimitQuery() would be nice here, but it causes BC issues if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } - $res =& $this->execute($sth, $params); + $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { - $res =& $this->query($query); + $res = $this->query($query); } if (DB::isError($res)) { @@ -1305,27 +1372,25 @@ class DB_common extends PEAR // {{{ getCol() /** - * Fetch a single column from a result set and return it as an + * Fetches a single column from a query result and returns it as an * indexed array * - * @param string $query the SQL query - * @param mixed $col which column to return (integer [column number, - * starting at 0] or string [column name]) - * @param mixed $params array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. + * @param string $query the SQL query + * @param mixed $col which column to return (integer [column number, + * starting at 0] or string [column name]) + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @return array an indexed array with the data from the first - * row at index 0, or a DB error code + * @return array the results as an array. A DB_Error object on failure. * * @see DB_common::query() - * @access public */ function &getCol($query, $col = 0, $params = array()) { - settype($params, 'array'); + $params = (array)$params; if (sizeof($params) > 0) { $sth = $this->prepare($query); @@ -1333,10 +1398,10 @@ class DB_common extends PEAR return $sth; } - $res =& $this->execute($sth, $params); + $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { - $res =& $this->query($query); + $res = $this->query($query); } if (DB::isError($res)) { @@ -1349,7 +1414,7 @@ class DB_common extends PEAR $ret = array(); } else { if (!array_key_exists($col, $row)) { - $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD); + $ret = $this->raiseError(DB_ERROR_NOSUCHFIELD); } else { $ret = array($row[$col]); while (is_array($row = $res->fetchRow($fetchmode))) { @@ -1371,7 +1436,7 @@ class DB_common extends PEAR // {{{ getAssoc() /** - * Fetch the entire result set of a query and return it as an + * Fetches an entire query result and returns it as an * associative array using the first column as the key * * If the result set contains more than two columns, the value @@ -1432,33 +1497,32 @@ class DB_common extends PEAR * Keep in mind that database functions in PHP usually return string * values for results regardless of the database's internal type. * - * @param string $query the SQL query - * @param boolean $force_array used only when the query returns + * @param string $query the SQL query + * @param bool $force_array used only when the query returns * exactly two columns. If true, the values * of the returned array will be one-element * arrays instead of scalars. - * @param mixed $params array, string or numeric data to be used in - * execution of the statement. Quantity of items - * passed must match quantity of placeholders in - * query: meaning 1 placeholder for non-array - * parameters or 1 placeholder per array element. - * @param int $fetchmode the fetch mode to use - * @param boolean $group if true, the values of the returned array - * is wrapped in another array. If the same - * key value (in the first column) repeats - * itself, the values will be appended to - * this array instead of overwriting the - * existing values. - * - * @return array associative array with results from the query. - * DB Error on failure. - * - * @access public + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of + * items passed must match quantity of + * placeholders in query: meaning 1 + * placeholder for non-array parameters or + * 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use + * @param bool $group if true, the values of the returned array + * is wrapped in another array. If the same + * key value (in the first column) repeats + * itself, the values will be appended to + * this array instead of overwriting the + * existing values. + * + * @return array the associative array containing the query results. + * A DB_Error object on failure. */ function &getAssoc($query, $force_array = false, $params = array(), $fetchmode = DB_FETCHMODE_DEFAULT, $group = false) { - settype($params, 'array'); + $params = (array)$params; if (sizeof($params) > 0) { $sth = $this->prepare($query); @@ -1466,10 +1530,10 @@ class DB_common extends PEAR return $sth; } - $res =& $this->execute($sth, $params); + $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { - $res =& $this->query($query); + $res = $this->query($query); } if (DB::isError($res)) { @@ -1481,7 +1545,7 @@ class DB_common extends PEAR $cols = $res->numCols(); if ($cols < 2) { - $tmp =& $this->raiseError(DB_ERROR_TRUNCATED); + $tmp = $this->raiseError(DB_ERROR_TRUNCATED); return $tmp; } @@ -1549,21 +1613,24 @@ class DB_common extends PEAR // {{{ getAll() /** - * Fetch all the rows returned from a query - * - * @param string $query the SQL query - * @param array $params array to be used in execution of the statement. - * Quantity of array elements must match quantity - * of placeholders in query. This function does - * NOT support scalars. - * @param int $fetchmode the fetch mode to use - * - * @return array an nested array. DB error on failure. - * - * @access public + * Fetches all of the rows from a query result + * + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of + * items passed must match quantity of + * placeholders in query: meaning 1 + * placeholder for non-array parameters or + * 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use: + * + DB_FETCHMODE_ORDERED + * + DB_FETCHMODE_ASSOC + * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED + * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED + * + * @return array the nested array. A DB_Error object on failure. */ - function &getAll($query, - $params = array(), + function &getAll($query, $params = array(), $fetchmode = DB_FETCHMODE_DEFAULT) { // compat check, the params and fetchmode parameters used to @@ -1590,13 +1657,13 @@ class DB_common extends PEAR return $sth; } - $res =& $this->execute($sth, $params); + $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { - $res =& $this->query($query); + $res = $this->query($query); } - if (DB::isError($res) || $res === DB_OK) { + if ($res === DB_OK || DB::isError($res)) { return $res; } @@ -1614,7 +1681,7 @@ class DB_common extends PEAR $res->free(); if (DB::isError($row)) { - $tmp =& $this->raiseError($row); + $tmp = $this->raiseError($row); return $tmp; } return $results; @@ -1624,14 +1691,14 @@ class DB_common extends PEAR // {{{ autoCommit() /** - * enable automatic Commit + * Enables or disables automatic commits * - * @param boolean $onoff - * @return mixed DB_Error + * @param bool $onoff true turns it on, false turns it off * - * @access public + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ - function autoCommit($onoff=false) + function autoCommit($onoff = false) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } @@ -1640,11 +1707,9 @@ class DB_common extends PEAR // {{{ commit() /** - * starts a Commit + * Commits the current transaction * - * @return mixed DB_Error - * - * @access public + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @@ -1655,11 +1720,9 @@ class DB_common extends PEAR // {{{ rollback() /** - * starts a rollback - * - * @return mixed DB_Error + * Reverts the current transaction * - * @access public + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @@ -1670,13 +1733,11 @@ class DB_common extends PEAR // {{{ numRows() /** - * Returns the number of rows in a result object - * - * @param object DB_Result the result object to check + * Determines the number of rows in a query result * - * @return mixed DB_Error or the number of rows + * @param resource $result the query result idenifier produced by PHP * - * @access public + * @return int the number of rows. A DB_Error object on failure. */ function numRows($result) { @@ -1687,11 +1748,11 @@ class DB_common extends PEAR // {{{ affectedRows() /** - * Returns the affected rows of a query + * Determines the number of rows affected by a data maniuplation query * - * @return mixed DB_Error or number of rows + * 0 is returned for queries that don't manipulate data. * - * @access public + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { @@ -1699,25 +1760,10 @@ class DB_common extends PEAR } // }}} - // {{{ errorNative() - - /** - * Returns an errormessage, provides by the database - * - * @return mixed DB_Error or message - * - * @access public - */ - function errorNative() - { - return $this->raiseError(DB_ERROR_NOT_CAPABLE); - } - - // }}} // {{{ getSequenceName() /** - * Generate the name used inside the database for a sequence + * Generates the name used inside the database for a sequence * * The createSequence() docblock contains notes about storing sequence * names. @@ -1726,9 +1772,9 @@ class DB_common extends PEAR * * @return string the sequence's name in the backend * + * @access protected * @see DB_common::createSequence(), DB_common::dropSequence(), * DB_common::nextID(), DB_common::setOption() - * @access private */ function getSequenceName($sqn) { @@ -1744,13 +1790,13 @@ class DB_common extends PEAR * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::dropSequence(), * DB_common::getSequenceName() - * @access public */ function nextId($seq_name, $ondemand = true) { @@ -1772,12 +1818,10 @@ class DB_common extends PEAR * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_common::nextID() - * @access public */ function createSequence($seq_name) { @@ -1792,11 +1836,10 @@ class DB_common extends PEAR * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_common::nextID() - * @access public */ function dropSequence($seq_name) { @@ -1804,6 +1847,118 @@ class DB_common extends PEAR } // }}} + // {{{ raiseError() + + /** + * Communicates an error and invoke error callbacks, etc + * + * Basically a wrapper for PEAR::raiseError without the message string. + * + * @param mixed integer error code, or a PEAR error object (all + * other parameters are ignored if this parameter is + * an object + * @param int error mode, see PEAR_Error docs + * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the + * error level (E_USER_NOTICE etc). If error mode is + * PEAR_ERROR_CALLBACK, this is the callback function, + * either as a function name, or as an array of an + * object and method name. For other error modes this + * parameter is ignored. + * @param string extra debug information. Defaults to the last + * query and native error code. + * @param mixed native error code, integer or string depending the + * backend + * + * @return object the PEAR_Error object + * + * @see PEAR_Error + */ + function &raiseError($code = DB_ERROR, $mode = null, $options = null, + $userinfo = null, $nativecode = null) + { + // The error is yet a DB error object + if (is_object($code)) { + // because we the static PEAR::raiseError, our global + // handler should be used if it is set + if ($mode === null && !empty($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + } + $tmp = PEAR::raiseError($code, null, $mode, $options, + null, null, true); + return $tmp; + } + + if ($userinfo === null) { + $userinfo = $this->last_query; + } + + if ($nativecode) { + $userinfo .= ' [nativecode=' . trim($nativecode) . ']'; + } else { + $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']'; + } + + $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, + 'DB_Error', true); + return $tmp; + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return mixed the DBMS' error code. A DB_Error object on failure. + */ + function errorNative() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones + * + * Uses the $errorcode_map property defined in each driver. + * + * @param string|int $nativecode the error code returned by the DBMS + * + * @return int the portable DB error code. Return DB_ERROR if the + * current driver doesn't have a mapping for the + * $nativecode submitted. + */ + function errorCode($nativecode) + { + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ errorMessage() + + /** + * Maps a DB error code to a textual message + * + * @param integer $dbcode the DB error code + * + * @return string the error message corresponding to the error code + * submitted. FALSE if the error code is unknown. + * + * @see DB::errorMessage() + */ + function errorMessage($dbcode) + { + return DB::errorMessage($this->errorcode_map[$dbcode]); + } + + // }}} // {{{ tableInfo() /** @@ -1920,11 +2075,11 @@ class DB_common extends PEAR * DB_TABLEINFO_FULL (which does both). * These are bitwise, so the first two can be * combined using |. + * * @return array an associative array with the information requested. - * If something goes wrong an error object is returned. + * A DB_Error object on failure. * * @see DB_common::setOption() - * @access public */ function tableInfo($result, $mode = null) { @@ -1940,7 +2095,11 @@ class DB_common extends PEAR // {{{ getTables() /** - * @deprecated Deprecated in release 1.2 or lower + * Lists the tables in the current database + * + * @return array the list of tables. A DB_Error object on failure. + * + * @deprecated Method deprecated some time before Release 1.2 */ function getTables() { @@ -1951,40 +2110,46 @@ class DB_common extends PEAR // {{{ getListOf() /** - * list internal DB info - * valid values for $type are db dependent, - * often: databases, users, view, functions + * Lists internal database information * - * @param string $type type of requested info + * @param string $type type of information being sought. + * Common items being sought are: + * tables, databases, users, views, functions + * Each DBMS's has its own capabilities. * - * @return mixed DB_Error or the requested data - * - * @access public + * @return array an array listing the items sought. + * A DB DB_Error object on failure. */ function getListOf($type) { $sql = $this->getSpecialQuery($type); - if ($sql === null) { // No support + if ($sql === null) { + $this->last_query = ''; return $this->raiseError(DB_ERROR_UNSUPPORTED); - } elseif (is_int($sql) || DB::isError($sql)) { // Previous error + } elseif (is_int($sql) || DB::isError($sql)) { + // Previous error return $this->raiseError($sql); - } elseif (is_array($sql)) { // Already the result + } elseif (is_array($sql)) { + // Already the result return $sql; } - return $this->getCol($sql); // Launch this query + // Launch this query + return $this->getCol($sql); } // }}} // {{{ getSpecialQuery() /** - * Returns the query needed to get some backend info + * Obtains the query string needed for listing a given type of objects * - * @param string $type What kind of info you want to retrieve + * @param string $type the kind of objects you want to retrieve * - * @return string The SQL query string + * @return string the SQL query string or null if the driver doesn't + * support the object type requested * - * @access public + * @access protected + * @see DB_common::getListOf() */ function getSpecialQuery($type) { @@ -1992,14 +2157,62 @@ class DB_common extends PEAR } // }}} + // {{{ nextQueryIsManip() + + /** + * Sets (or unsets) a flag indicating that the next query will be a + * manipulation query, regardless of the usual DB::isManip() heuristics. + * + * @param boolean true to set the flag overriding the isManip() behaviour, + * false to clear it and fall back onto isManip() + * + * @return void + * + * @access public + */ + function nextQueryIsManip($manip) + { + $this->_next_query_manip = $manip; + } + + // }}} + // {{{ _checkManip() + + /** + * Checks if the given query is a manipulation query. This also takes into + * account the _next_query_manip flag and sets the _last_query_manip flag + * (and resets _next_query_manip) according to the result. + * + * @param string The query to check. + * + * @return boolean true if the query is a manipulation query, false + * otherwise + * + * @access protected + */ + function _checkManip($query) + { + if ($this->_next_query_manip || DB::isManip($query)) { + $this->_last_query_manip = true; + } else { + $this->_last_query_manip = false; + } + $this->_next_query_manip = false; + return $this->_last_query_manip; + $manip = $this->_next_query_manip; + } + + // }}} // {{{ _rtrimArrayValues() /** - * Right trim all strings in an array + * Right-trims all strings in an array + * + * @param array $array the array to be trimmed (passed by reference) * - * @param array $array the array to be trimmed (passed by reference) * @return void - * @access private + * + * @access protected */ function _rtrimArrayValues(&$array) { @@ -2014,11 +2227,13 @@ class DB_common extends PEAR // {{{ _convertNullArrayValuesToEmpty() /** - * Convert all null values in an array to empty strings + * Converts all null values in an array to empty strings * * @param array $array the array to be de-nullified (passed by reference) + * * @return void - * @access private + * + * @access protected */ function _convertNullArrayValuesToEmpty(&$array) { @@ -2030,22 +2245,6 @@ class DB_common extends PEAR } // }}} - - // {{{ getLastId() - - /** - * Returns the last insert ID - * - * @return int - * - * @access public - */ - function getLastId() - { - return $this->_getLastId(); - } - - // }}} } /* diff --git a/thirdparty/pear/DB/dbase.php b/thirdparty/pear/DB/dbase.php old mode 100644 new mode 100755 index a5ad09f..cdd2e59 --- a/thirdparty/pear/DB/dbase.php +++ b/thirdparty/pear/DB/dbase.php @@ -1,102 +1,264 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// XXX legend: -// You have to compile your PHP with the --enable-dbase option +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's dbase extension + * for interacting with dBase databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: dbase.php,v 1.45 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's dbase - * extension. + * The methods PEAR DB uses to interact with PHP's dbase extension + * for interacting with dBase databases + * + * These methods overload the ones declared in DB_common. * - * @package DB - * @version $Id$ - * @category Database - * @author Stig Bakken + * @category Database + * @package DB + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_dbase extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'dbase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'dbase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => false, + 'new_link' => false, + 'numrows' => true, + 'pconnect' => false, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * A means of emulating result resources + * @var array + */ var $res_row = array(); + + /** + * The quantity of results so far + * + * For emulating result resources. + * + * @var integer + */ var $result = 0; + /** + * Maps dbase data type id's to human readable strings + * + * The human readable values are based on the output of PHP's + * dbase_get_header_info() function. + * + * @var array + * @since Property available since Release 1.7.0 + */ + var $types = array( + 'C' => 'character', + 'D' => 'date', + 'L' => 'boolean', + 'M' => 'memo', + 'N' => 'number', + ); + + // }}} // {{{ constructor /** - * DB_mysql constructor. + * This constructor calls $this->DB_common() * - * @access public + * @return void */ function DB_dbase() { $this->DB_common(); - $this->phptype = 'dbase'; - $this->dbsyntax = 'dbase'; - $this->features = array( - 'prepare' => false, - 'pconnect' => false, - 'transactions' => false, - 'limit' => false - ); - $this->errorcode_map = array(); } // }}} // {{{ connect() - function connect($dsninfo, $persistent = false) + /** + * Connect to the database and create it if it doesn't exist + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's dbase driver supports the following extra DSN options: + * + mode An integer specifying the read/write mode to use + * (0 = read only, 1 = write only, 2 = read/write). + * Available since PEAR DB 1.7.0. + * + fields An array of arrays that PHP's dbase_create() function needs + * to create a new database. This information is used if the + * dBase file specified in the "database" segment of the DSN + * does not exist. For more info, see the PHP manual's + * {@link http://php.net/dbase_create dbase_create()} page. + * Available since PEAR DB 1.7.0. + * + * Example of how to connect and establish a new dBase file if necessary: + * + * require_once 'DB.php'; + * + * $dsn = array( + * 'phptype' => 'dbase', + * 'database' => '/path/and/name/of/dbase/file', + * 'mode' => 2, + * 'fields' => array( + * array('a', 'N', 5, 0), + * array('b', 'C', 40), + * array('c', 'C', 255), + * array('d', 'C', 20), + * ), + * ); + * $options = array( + * 'debug' => 2, + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('dbase')) { + if (!PEAR::loadExtension('dbase')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $ini = ini_get('track_errors'); - if ($ini) { - $conn = @dbase_open($dsninfo['database'], 0); - } else { - ini_set('track_errors', 1); - $conn = @dbase_open($dsninfo['database'], 0); - ini_set('track_errors', $ini); + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; } - if (!$conn) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, - null, null, strip_tags($php_errormsg)); + + /* + * Turn track_errors on for entire script since $php_errormsg + * is the only way to find errors from the dbase extension. + */ + @ini_set('track_errors', 1); + $php_errormsg = ''; + + if (!file_exists($dsn['database'])) { + $this->dsn['mode'] = 2; + if (empty($dsn['fields']) || !is_array($dsn['fields'])) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'the dbase file does not exist and ' + . 'it could not be created because ' + . 'the "fields" element of the DSN ' + . 'is not properly set'); + } + $this->connection = @dbase_create($dsn['database'], + $dsn['fields']); + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'the dbase file does not exist and ' + . 'the attempt to create it failed: ' + . $php_errormsg); + } + } else { + if (!isset($this->dsn['mode'])) { + $this->dsn['mode'] = 0; + } + $this->connection = @dbase_open($dsn['database'], + $this->dsn['mode']); + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } } - $this->connection = $conn; return DB_OK; } // }}} // {{{ disconnect() + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ function disconnect() { $ret = @dbase_close($this->connection); @@ -111,7 +273,7 @@ class DB_dbase extends DB_common { // emulate result resources $this->res_row[(int)$this->result] = 0; - $tmp =& new DB_result($this, $this->result++); + $tmp = new DB_result($this, $this->result++); return $tmp; } @@ -119,24 +281,26 @@ class DB_dbase extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum === null) { $rownum = $this->res_row[(int)$result]++; @@ -162,8 +326,41 @@ class DB_dbase extends DB_common } // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set. + * + * This method is a no-op for dbase, as there aren't result resources in + * the same sense as most other database backends. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return true; + } + + // }}} // {{{ numCols() + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ function numCols($foo) { return @dbase_numfields($this->connection); @@ -172,47 +369,135 @@ class DB_dbase extends DB_common // }}} // {{{ numRows() + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ function numRows($foo) { return @dbase_numrecords($this->connection); } // }}} - // {{{ quoteSmart() + // {{{ quoteBoolean() + + /** + * Formats a boolean value for use within a query in a locale-independent + * manner. + * + * @param boolean the boolean value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteBoolean($boolean) { + return $boolean ? 'T' : 'F'; + } + + // }}} + // {{{ tableInfo() /** - * Format input so it can be safely used in a query + * Returns information about the current database * - * @param mixed $in data to be quoted + * @param mixed $result THIS IS UNUSED IN DBASE. The current database + * is examined regardless of what is provided here. + * @param int $mode a valid tableInfo mode * - * @return mixed Submitted variable's type = returned value: - * + null = the string NULL - * + boolean = T if true or - * F if false. Use the Logical - * data type. - * + integer or double = the unquoted number - * + other (including strings and numeric strings) = - * the data with single quotes escaped by preceeding - * single quotes then the whole string is encapsulated - * between single quotes + * @return array an associative array with the information requested. + * A DB_Error object on failure. * - * @internal + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 */ - function quoteSmart($in) + function tableInfo($result = null, $mode = null) { - if (is_int($in) || is_double($in)) { - return $in; - } elseif (is_bool($in)) { - return $in ? 'T' : 'F'; - } elseif (is_null($in)) { - return 'NULL'; + if (function_exists('dbase_get_header_info')) { + $id = @dbase_get_header_info($this->connection); + if (!$id && $php_errormsg) { + return $this->raiseError(DB_ERROR, + null, null, null, + $php_errormsg); + } + } else { + /* + * This segment for PHP 4 is loosely based on code by + * Hadi Rusiah in the comments on + * the dBase reference page in the PHP manual. + */ + $db = @fopen($this->dsn['database'], 'r'); + if (!$db) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + + $id = array(); + $i = 0; + + $line = fread($db, 32); + while (!feof($db)) { + $line = fread($db, 32); + if (substr($line, 0, 1) == chr(13)) { + break; + } else { + $pos = strpos(substr($line, 0, 10), chr(0)); + $pos = ($pos == 0 ? 10 : $pos); + $id[$i] = array( + 'name' => substr($line, 0, $pos), + 'type' => $this->types[substr($line, 11, 1)], + 'length' => ord(substr($line, 16, 1)), + 'precision' => ord(substr($line, 17, 1)), + ); + } + $i++; + } + + fclose($db); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; } else { - return "'" . $this->escapeSimple($in) . "'"; + $case_func = 'strval'; + } + + $res = array(); + $count = count($id); + + if ($mode) { + $res['num_fields'] = $count; } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $this->dsn['database'], + 'name' => $case_func($id[$i]['name']), + 'type' => $id[$i]['type'], + 'len' => $id[$i]['length'], + 'flags' => '' + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + return $res; } // }}} - } /* diff --git a/thirdparty/pear/DB/fbsql.php b/thirdparty/pear/DB/fbsql.php old mode 100644 new mode 100755 index 398fff6..7749a98 --- a/thirdparty/pear/DB/fbsql.php +++ b/thirdparty/pear/DB/fbsql.php @@ -1,144 +1,194 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// XXX legend: -// -// XXX ERRORMSG: The error message from the fbsql function should -// be registered here. -// -// TODO/wishlist: -// longReadlen -// binmode +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's fbsql extension + * for interacting with FrontBase databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Frank M. Kromann + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: fbsql.php,v 1.88 2007/07/06 05:19:21 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's FrontBase - * extension. + * The methods PEAR DB uses to interact with PHP's fbsql extension + * for interacting with FrontBase databases * - * @package DB - * @version $Id$ - * @category Database - * @author Frank M. Kromann + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Frank M. Kromann + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB + * @since Class functional since Release 1.7.0 */ class DB_fbsql extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'fbsql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'fbsql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 22 => DB_ERROR_SYNTAX, + 85 => DB_ERROR_ALREADY_EXISTS, + 108 => DB_ERROR_SYNTAX, + 116 => DB_ERROR_NOSUCHTABLE, + 124 => DB_ERROR_VALUE_COUNT_ON_ROW, + 215 => DB_ERROR_NOSUCHFIELD, + 217 => DB_ERROR_INVALID_NUMBER, + 226 => DB_ERROR_NOSUCHFIELD, + 231 => DB_ERROR_INVALID, + 239 => DB_ERROR_TRUNCATED, + 251 => DB_ERROR_SYNTAX, + 266 => DB_ERROR_NOT_FOUND, + 357 => DB_ERROR_CONSTRAINT_NOT_NULL, + 358 => DB_ERROR_CONSTRAINT, + 360 => DB_ERROR_CONSTRAINT, + 361 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); - var $num_rows = array(); - var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + // }}} // {{{ constructor /** - * DB_fbsql constructor. + * This constructor calls $this->DB_common() * - * @access public + * @return void */ function DB_fbsql() { $this->DB_common(); - $this->phptype = 'fbsql'; - $this->dbsyntax = 'fbsql'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => true, - 'limit' => 'emulate' - ); - $this->errorcode_map = array( - 1004 => DB_ERROR_CANNOT_CREATE, - 1005 => DB_ERROR_CANNOT_CREATE, - 1006 => DB_ERROR_CANNOT_CREATE, - 1007 => DB_ERROR_ALREADY_EXISTS, - 1008 => DB_ERROR_CANNOT_DROP, - 1046 => DB_ERROR_NODBSELECTED, - 1050 => DB_ERROR_ALREADY_EXISTS, - 1051 => DB_ERROR_NOSUCHTABLE, - 1054 => DB_ERROR_NOSUCHFIELD, - 1062 => DB_ERROR_ALREADY_EXISTS, - 1064 => DB_ERROR_SYNTAX, - 1100 => DB_ERROR_NOT_LOCKED, - 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, - 1146 => DB_ERROR_NOSUCHTABLE, - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent - * @access public - * @return int DB_OK on success, a DB error on failure + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('fbsql')) { + if (!PEAR::loadExtension('fbsql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array( + $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + ); - $php_errormsg = ''; $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect'; - if ($dbhost && $dsninfo['username'] && $dsninfo['password']) { - $conn = @$connect_function($dbhost, $dsninfo['username'], - $dsninfo['password']); - } elseif ($dbhost && $dsninfo['username']) { - $conn = @$connect_function($dbhost, $dsninfo['username']); - } elseif ($dbhost) { - $conn = @$connect_function($dbhost); + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); } else { - $conn = false; + @ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + @ini_set('track_errors', $ini); } - if (!$conn) { - if (empty($php_errormsg)) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED); - } else { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $php_errormsg); - } + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); } - if ($dsninfo['database']) { - if (!fbsql_select_db($dsninfo['database'], $conn)) { + if ($dsn['database']) { + if (!@fbsql_select_db($dsn['database'], $this->connection)) { return $this->fbsqlRaiseError(); } } - $this->connection = $conn; return DB_OK; } @@ -146,11 +196,9 @@ class DB_fbsql extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. - * - * @access public + * Disconnects from the database server * - * @return bool true on success, false if not connected. + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -163,16 +211,13 @@ class DB_fbsql extends DB_common // {{{ simpleQuery() /** - * Send a query to fbsql and return the results as a fbsql resource - * identifier. + * Sends a query to the database server * - * @param the SQL query + * @param string the SQL query string * - * @access public - * - * @return mixed returns a valid fbsql result for successful SELECT - * queries, DB_OK for other successful queries. A DB error is - * returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { @@ -184,14 +229,9 @@ class DB_fbsql extends DB_common } // Determine which queries that should return data, and which // should return an error code only. - if (DB::isManip($query)) { + if ($this->_checkManip($query)) { return DB_OK; } - $numrows = $this->numrows($result); - if (is_object($numrows)) { - return $numrows; - } - $this->num_rows[(int)$result] = $numrows; return $result; } @@ -216,24 +256,26 @@ class DB_fbsql extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@fbsql_data_seek($result, $rownum)) { @@ -249,11 +291,7 @@ class DB_fbsql extends DB_common $arr = @fbsql_fetch_row($result); } if (!$arr) { - $errno = @fbsql_errno($this->connection); - if (!$errno) { - return null; - } - return $this->fbsqlRaiseError($errno); + return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); @@ -268,22 +306,34 @@ class DB_fbsql extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set * - * @param $result fbsql result identifier + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return bool true on success, false if $result is invalid + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() */ function freeResult($result) { - return @fbsql_free_result($result); + return is_resource($result) ? fbsql_free_result($result) : false; } // }}} // {{{ autoCommit() + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ function autoCommit($onoff=false) { if ($onoff) { @@ -296,39 +346,51 @@ class DB_fbsql extends DB_common // }}} // {{{ commit() + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ function commit() { - @fbsql_commit(); + @fbsql_commit($this->connection); } // }}} // {{{ rollback() + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ function rollback() { - @fbsql_rollback(); + @fbsql_rollback($this->connection); } // }}} // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set * - * @param $result fbsql result identifier + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. * - * @return int the number of columns per row in $result + * @see DB_result::numCols() */ function numCols($result) { $cols = @fbsql_num_fields($result); - if (!$cols) { return $this->fbsqlRaiseError(); } - return $cols; } @@ -336,13 +398,17 @@ class DB_fbsql extends DB_common // {{{ numRows() /** - * Get the number of rows in a result set. + * Gets the number of rows in a result set * - * @param $result fbsql result identifier + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return int the number of rows in $result + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() */ function numRows($result) { @@ -357,14 +423,15 @@ class DB_fbsql extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the data manipulation - * query. For other queries, this function returns 0. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return number of rows affected by the last query + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (DB::isManip($this->last_query)) { + if ($this->_last_query_manip) { $result = @fbsql_affected_rows($this->connection); } else { $result = 0; @@ -373,22 +440,6 @@ class DB_fbsql extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @access public - * - * @return int native fbsql error code - */ - function errorNative() - { - return @fbsql_errno($this->connection); - } - - // }}} // {{{ nextId() /** @@ -396,20 +447,22 @@ class DB_fbsql extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_fbsql::createSequence(), DB_fbsql::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); - $repeat = 0; do { - $result = $this->query("INSERT INTO ${seqname} (id) VALUES (NULL)"); + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query('SELECT UNIQUE FROM ' . $seqname); + $this->popErrorHandling(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { $repeat = 1; @@ -422,9 +475,10 @@ class DB_fbsql extends DB_common } } while ($repeat); if (DB::isError($result)) { - return $result; + return $this->fbsqlRaiseError(); } - return @fbsql_insert_id($this->connection); + $result->fetchInto($tmp, DB_FETCHMODE_ORDERED); + return $tmp[0]; } /** @@ -432,19 +486,21 @@ class DB_fbsql extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_fbsql::nextID(), DB_fbsql::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); - return $this->query("CREATE TABLE ${seqname} ". - '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. - ' PRIMARY KEY(id))'); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER NOT NULL,' + . ' PRIMARY KEY(id))'); + if ($res) { + $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname); + } + return $res; } // }}} @@ -455,77 +511,93 @@ class DB_fbsql extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_fbsql::nextID(), DB_fbsql::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP TABLE ${seqname} RESTRICT"); + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name) + . ' RESTRICT'); } // }}} - // {{{ modifyQuery() + // {{{ modifyLimitQuery() - function modifyQuery($query) + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) { - if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { - // "DELETE FROM table" gives 0 affected rows in fbsql. - // This little hack lets you know how many rows were deleted. - if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { - $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', - 'DELETE FROM \1 WHERE 1=1', $query); - } + if (DB::isManip($query) || $this->_next_query_manip) { + return preg_replace('/^([\s(])*SELECT/i', + "\\1SELECT TOP($count)", $query); + } else { + return preg_replace('/([\s(])*SELECT/i', + "\\1SELECT TOP($from, $count)", $query); } - return $query; } // }}} - // {{{ quoteSmart() + // {{{ quoteBoolean() /** - * Format input so it can be safely used in a query - * - * @param mixed $in data to be quoted - * - * @return mixed Submitted variable's type = returned value: - * + null = the string NULL - * + boolean = string TRUE or FALSE - * + integer or double = the unquoted number - * + other (including strings and numeric strings) = - * the data escaped according to MySQL's settings - * then encapsulated between single quotes + * Formats a boolean value for use within a query in a locale-independent + * manner. * - * @internal + * @param boolean the boolean value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. */ - function quoteSmart($in) - { - if (is_int($in) || is_double($in)) { - return $in; - } elseif (is_bool($in)) { - return $in ? 'TRUE' : 'FALSE'; - } elseif (is_null($in)) { - return 'NULL'; - } else { - return "'" . $this->escapeSimple($in) . "'"; - } + function quoteBoolean($boolean) { + return $boolean ? 'TRUE' : 'FALSE'; } + + // }}} + // {{{ quoteFloat() + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + // }}} // {{{ fbsqlRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. - * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see DB_common::errorCode() - * @see DB_common::raiseError() + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_fbsql::errorNative(), DB_common::errorCode() */ function fbsqlRaiseError($errno = null) { @@ -533,33 +605,43 @@ class DB_fbsql extends DB_common $errno = $this->errorCode(fbsql_errno($this->connection)); } return $this->raiseError($errno, null, null, null, - @fbsql_error($this->connection)); + @fbsql_error($this->connection)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + return @fbsql_errno($this->connection); } // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() */ - function tableInfo($result, $mode = null) { - if (isset($result->result)) { - /* - * Probably received a result object. - * Extract the result resource identifier. - */ - $id = $result->result; - $got_string = false; - } elseif (is_string($result)) { + function tableInfo($result, $mode = null) + { + if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. @@ -567,6 +649,13 @@ class DB_fbsql extends DB_common $id = @fbsql_list_fields($this->dsn['database'], $result, $this->connection); $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; } else { /* * Probably received a result resource identifier. @@ -588,32 +677,25 @@ class DB_fbsql extends DB_common } $count = @fbsql_num_fields($id); + $res = array(); - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i)); - $res[$i]['name'] = $case_func(@fbsql_field_name($id, $i)); - $res[$i]['type'] = @fbsql_field_type($id, $i); - $res[$i]['len'] = @fbsql_field_len($id, $i); - $res[$i]['flags'] = @fbsql_field_flags($id, $i); + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $case_func(@fbsql_field_table($id, $i)), + 'name' => $case_func(@fbsql_field_name($id, $i)), + 'type' => @fbsql_field_type($id, $i), + 'len' => @fbsql_field_len($id, $i), + 'flags' => @fbsql_field_flags($id, $i), + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; } - } else { // full - $res["num_fields"]= $count; - - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i)); - $res[$i]['name'] = $case_func(@fbsql_field_name($id, $i)); - $res[$i]['type'] = @fbsql_field_type($id, $i); - $res[$i]['len'] = @fbsql_field_len($id, $i); - $res[$i]['flags'] = @fbsql_field_flags($id, $i); - - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -628,15 +710,47 @@ class DB_fbsql extends DB_common // {{{ getSpecialQuery() /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': - return 'select "table_name" from information_schema.tables'; + return 'SELECT "table_name" FROM information_schema.tables' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk AND' + . ' "table_type" = \'BASE TABLE\'' + . ' AND "schema_name" = current_schema'; + case 'views': + return 'SELECT "table_name" FROM information_schema.tables' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk AND' + . ' "table_type" = \'VIEW\'' + . ' AND "schema_name" = current_schema'; + case 'users': + return 'SELECT "user_name" from information_schema.users'; + case 'functions': + return 'SELECT "routine_name" FROM' + . ' information_schema.psm_routines' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk' + . ' AND "routine_kind"=\'FUNCTION\'' + . ' AND "schema_name" = current_schema'; + case 'procedures': + return 'SELECT "routine_name" FROM' + . ' information_schema.psm_routines' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk' + . ' AND "routine_kind"=\'PROCEDURE\'' + . ' AND "schema_name" = current_schema'; default: return null; } diff --git a/thirdparty/pear/DB/ibase.php b/thirdparty/pear/DB/ibase.php old mode 100644 new mode 100755 index e2eddb0..7836064 --- a/thirdparty/pear/DB/ibase.php +++ b/thirdparty/pear/DB/ibase.php @@ -1,132 +1,260 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// Bugs: -// - If dbsyntax is not firebird, the limitQuery may fail +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's interbase extension + * for interacting with Interbase and Firebird databases + * + * While this class works with PHP 4, PHP's InterBase extension is + * unstable in PHP 4. Use PHP 5. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: ibase.php,v 1.116 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's Interbase - * extension. + * The methods PEAR DB uses to interact with PHP's interbase extension + * for interacting with Interbase and Firebird databases + * + * These methods overload the ones declared in DB_common. + * + * While this class works with PHP 4, PHP's InterBase extension is + * unstable in PHP 4. Use PHP 5. * - * @package DB - * @version $Id$ - * @category Database - * @author Sterling Hughes + * NOTICE: limitQuery() only works for Firebird. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB + * @since Class became stable in Release 1.7.0 */ class DB_ibase extends DB_common { - // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'ibase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'ibase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * NOTE: only firebird supports limit. + * + * @var array + */ + var $features = array( + 'limit' => false, + 'new_link' => false, + 'numrows' => 'emulate', + 'pconnect' => true, + 'prepare' => true, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + -104 => DB_ERROR_SYNTAX, + -150 => DB_ERROR_ACCESS_VIOLATION, + -151 => DB_ERROR_ACCESS_VIOLATION, + -155 => DB_ERROR_NOSUCHTABLE, + -157 => DB_ERROR_NOSUCHFIELD, + -158 => DB_ERROR_VALUE_COUNT_ON_ROW, + -170 => DB_ERROR_MISMATCH, + -171 => DB_ERROR_MISMATCH, + -172 => DB_ERROR_INVALID, + // -204 => // Covers too many errors, need to use regex on msg + -205 => DB_ERROR_NOSUCHFIELD, + -206 => DB_ERROR_NOSUCHFIELD, + -208 => DB_ERROR_INVALID, + -219 => DB_ERROR_NOSUCHTABLE, + -297 => DB_ERROR_CONSTRAINT, + -303 => DB_ERROR_INVALID, + -413 => DB_ERROR_INVALID_NUMBER, + -530 => DB_ERROR_CONSTRAINT, + -551 => DB_ERROR_ACCESS_VIOLATION, + -552 => DB_ERROR_ACCESS_VIOLATION, + // -607 => // Covers too many errors, need to use regex on msg + -625 => DB_ERROR_CONSTRAINT_NOT_NULL, + -803 => DB_ERROR_CONSTRAINT, + -804 => DB_ERROR_VALUE_COUNT_ON_ROW, + // -902 => // Covers too many errors, need to use regex on msg + -904 => DB_ERROR_CONNECT_FAILED, + -922 => DB_ERROR_NOSUCHDB, + -923 => DB_ERROR_CONNECT_FAILED, + -924 => DB_ERROR_CONNECT_FAILED + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $autocommit = 1; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The prepared statement handle from the most recently executed statement + * + * {@internal Mainly here because the InterBase/Firebird API is only + * able to retrieve data from result sets if the statemnt handle is + * still in scope.}} + * + * @var resource + */ + var $last_stmt; + + /** + * Is the given prepared statement a data manipulation query? + * @var array + * @access private + */ var $manip_query = array(); + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_ibase() { $this->DB_common(); - $this->phptype = 'ibase'; - $this->dbsyntax = 'ibase'; - $this->features = array( - 'prepare' => true, - 'pconnect' => true, - 'transactions' => true, - 'limit' => false - ); - // just a few of the tons of Interbase error codes listed in the - // Language Reference section of the Interbase manual - $this->errorcode_map = array( - -104 => DB_ERROR_SYNTAX, - -150 => DB_ERROR_ACCESS_VIOLATION, - -151 => DB_ERROR_ACCESS_VIOLATION, - -155 => DB_ERROR_NOSUCHTABLE, - 88 => DB_ERROR_NOSUCHTABLE, - -157 => DB_ERROR_NOSUCHFIELD, - -158 => DB_ERROR_VALUE_COUNT_ON_ROW, - -170 => DB_ERROR_MISMATCH, - -171 => DB_ERROR_MISMATCH, - -172 => DB_ERROR_INVALID, - -204 => DB_ERROR_INVALID, - -205 => DB_ERROR_NOSUCHFIELD, - -206 => DB_ERROR_NOSUCHFIELD, - -208 => DB_ERROR_INVALID, - -219 => DB_ERROR_NOSUCHTABLE, - -297 => DB_ERROR_CONSTRAINT, - -530 => DB_ERROR_CONSTRAINT, - -607 => DB_ERROR_NOSUCHTABLE, - -803 => DB_ERROR_CONSTRAINT, - -551 => DB_ERROR_ACCESS_VIOLATION, - -552 => DB_ERROR_ACCESS_VIOLATION, - -922 => DB_ERROR_NOSUCHDB, - -923 => DB_ERROR_CONNECT_FAILED, - -924 => DB_ERROR_CONNECT_FAILED - ); } // }}} // {{{ connect() - function connect($dsninfo, $persistent = false) + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's ibase driver supports the following extra DSN options: + * + buffers The number of database buffers to allocate for the + * server-side cache. + * + charset The default character set for a database. + * + dialect The default SQL dialect for any statement + * executed within a connection. Defaults to the + * highest one supported by client libraries. + * Functional only with InterBase 6 and up. + * + role Functional only with InterBase 5 and up. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('interbase')) { + if (!PEAR::loadExtension('interbase')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $dbhost = $dsninfo['hostspec'] ? - ($dsninfo['hostspec'] . ':' . $dsninfo['database']) : - $dsninfo['database']; + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + if ($this->dbsyntax == 'firebird') { + $this->features['limit'] = 'alter'; + } + + $params = array( + $dsn['hostspec'] + ? ($dsn['hostspec'] . ':' . $dsn['database']) + : $dsn['database'], + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + isset($dsn['charset']) ? $dsn['charset'] : null, + isset($dsn['buffers']) ? $dsn['buffers'] : null, + isset($dsn['dialect']) ? $dsn['dialect'] : null, + isset($dsn['role']) ? $dsn['role'] : null, + ); $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; - $params = array(); - $params[] = $dbhost; - $params[] = $dsninfo['username'] ? $dsninfo['username'] : null; - $params[] = $dsninfo['password'] ? $dsninfo['password'] : null; - $params[] = isset($dsninfo['charset']) ? $dsninfo['charset'] : null; - $params[] = isset($dsninfo['buffers']) ? $dsninfo['buffers'] : null; - $params[] = isset($dsninfo['dialect']) ? $dsninfo['dialect'] : null; - $params[] = isset($dsninfo['role']) ? $dsninfo['role'] : null; - - $conn = @call_user_func_array($connect_function, $params); - if (!$conn) { + $this->connection = @call_user_func_array($connect_function, $params); + if (!$this->connection) { return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); } - $this->connection = $conn; - if ($this->dsn['dbsyntax'] == 'firebird') { - $this->features['limit'] = 'alter'; - } return DB_OK; } // }}} // {{{ disconnect() + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ function disconnect() { $ret = @ibase_close($this->connection); @@ -137,46 +265,63 @@ class DB_ibase extends DB_common // }}} // {{{ simpleQuery() + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); $result = @ibase_query($this->connection, $query); + if (!$result) { return $this->ibaseRaiseError(); } if ($this->autocommit && $ismanip) { @ibase_commit($this->connection); } - // Determine which queries that should return data, and which - // should return an error code only. - return $ismanip ? DB_OK : $result; + if ($ismanip) { + $this->affected = $result; + return DB_OK; + } else { + $this->affected = 0; + return $result; + } } // }}} // {{{ modifyLimitQuery() /** - * This method is used by backends to alter limited queries - * Uses the new FIRST n SKIP n Firebird 1.0 syntax, so it is - * only compatible with Firebird 1.x + * Adds LIMIT clauses to a query string according to current DBMS standards * - * @param string $query query to modify - * @param integer $from the row to start to fetching - * @param integer $count the numbers of rows to fetch + * Only works with Firebird. * - * @return the new (modified) query - * @author Ludovico Magnocavallo - * @access private + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { if ($this->dsn['dbsyntax'] == 'firebird') { - //$from++; // SKIP starts from 1, ie SKIP 1 starts from the first record - // (cox) Seems that SKIP starts in 0 - $query = preg_replace('/^\s*select\s(.*)$/is', - "SELECT FIRST $count SKIP $from $1", $query); + $query = preg_replace('/^([\s(])*SELECT/i', + "SELECT FIRST $count SKIP $from", $query); } return $query; } @@ -202,24 +347,26 @@ class DB_ibase extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); @@ -237,11 +384,7 @@ class DB_ibase extends DB_common $arr = @ibase_fetch_row($result); } if (!$arr) { - if ($errmsg = @ibase_errmsg()) { - return $this->ibaseRaiseError(null, $errmsg); - } else { - return null; - } + return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); @@ -255,9 +398,22 @@ class DB_ibase extends DB_common // }}} // {{{ freeResult() + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ function freeResult($result) { - return @ibase_free_result($result); + return is_resource($result) ? ibase_free_result($result) : false; } // }}} @@ -265,13 +421,43 @@ class DB_ibase extends DB_common function freeQuery($query) { - @ibase_free_query($query); - return true; + return is_resource($query) ? ibase_free_query($query) : false; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (is_integer($this->affected)) { + return $this->affected; + } + return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ numCols() + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ function numCols($result) { $cols = @ibase_num_fields($result); @@ -335,8 +521,14 @@ class DB_ibase extends DB_common $this->last_query = $query; $newquery = $this->modifyQuery($newquery); $stmt = @ibase_prepare($this->connection, $newquery); - $this->prepare_types[(int)$stmt] = $types; - $this->manip_query[(int)$stmt] = DB::isManip($query); + + if ($stmt === false) { + $stmt = $this->ibaseRaiseError(); + } else { + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + } + return $stmt; } @@ -358,13 +550,12 @@ class DB_ibase extends DB_common */ function &execute($stmt, $data = array()) { - if (!is_array($data)) { - $data = array($data); - } + $data = (array)$data; + $this->last_parameters = $data; - $types =& $this->prepare_types[(int)$stmt]; + $types = $this->prepare_types[(int)$stmt]; if (count($types) != count($data)) { - $tmp =& $this->raiseError(DB_ERROR_MISMATCH); + $tmp = $this->raiseError(DB_ERROR_MISMATCH); return $tmp; } @@ -383,7 +574,7 @@ class DB_ibase extends DB_common } elseif ($types[$i] == DB_PARAM_OPAQUE) { $fp = @fopen($data[$key], 'rb'); if (!$fp) { - $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); return $tmp; } $data[$key] = fread($fp, filesize($data[$key])); @@ -396,34 +587,45 @@ class DB_ibase extends DB_common $res = call_user_func_array('ibase_execute', $data); if (!$res) { - $tmp =& $this->ibaseRaiseError(); + $tmp = $this->ibaseRaiseError(); return $tmp; } /* XXX need this? if ($this->autocommit && $this->manip_query[(int)$stmt]) { @ibase_commit($this->connection); }*/ - if ($this->manip_query[(int)$stmt]) { + $this->last_stmt = $stmt; + if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { + $this->_last_query_manip = true; + $this->_next_query_manip = false; $tmp = DB_OK; } else { - $tmp =& new DB_result($this, $res); + $this->_last_query_manip = false; + $tmp = new DB_result($this, $res); } return $tmp; } /** - * Free the internal resources associated with a prepared query. + * Frees the internal resources associated with a prepared query * - * @param $stmt The interbase_query resource type + * @param resource $stmt the prepared statement's PHP resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. * - * @return bool true on success, false if $result is invalid + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_ibase::prepare() */ - function freePrepared($stmt) + function freePrepared($stmt, $free_resource = true) { if (!is_resource($stmt)) { return false; } - @ibase_free_query($stmt); + if ($free_resource) { + @ibase_free_query($stmt); + } unset($this->prepare_tokens[(int)$stmt]); unset($this->prepare_types[(int)$stmt]); unset($this->manip_query[(int)$stmt]); @@ -433,6 +635,14 @@ class DB_ibase extends DB_common // }}} // {{{ autoCommit() + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ function autoCommit($onoff = false) { $this->autocommit = $onoff ? 1 : 0; @@ -442,6 +652,11 @@ class DB_ibase extends DB_common // }}} // {{{ commit() + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ function commit() { return @ibase_commit($this->connection); @@ -450,6 +665,11 @@ class DB_ibase extends DB_common // }}} // {{{ rollback() + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ function rollback() { return @ibase_rollback($this->connection); @@ -460,7 +680,9 @@ class DB_ibase extends DB_common function transactionInit($trans_args = 0) { - return $trans_args ? @ibase_trans($trans_args, $this->connection) : @ibase_trans(); + return $trans_args + ? @ibase_trans($trans_args, $this->connection) + : @ibase_trans(); } // }}} @@ -471,13 +693,13 @@ class DB_ibase extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_ibase::createSequence(), DB_ibase::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -485,7 +707,7 @@ class DB_ibase extends DB_common $repeat = 0; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); - $result =& $this->query("SELECT GEN_ID(${sqn}, 1) " + $result = $this->query("SELECT GEN_ID(${sqn}, 1) " . 'FROM RDB$GENERATORS ' . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); $this->popErrorHandling(); @@ -511,11 +733,14 @@ class DB_ibase extends DB_common // {{{ createSequence() /** - * Create the sequence + * Creates a new sequence * - * @param string $seq_name the name of the sequence - * @return mixed DB_OK on success or DB error on error - * @access public + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_ibase::nextID(), DB_ibase::dropSequence() */ function createSequence($seq_name) { @@ -531,30 +756,37 @@ class DB_ibase extends DB_common // {{{ dropSequence() /** - * Drop a sequence + * Deletes a sequence * - * @param string $seq_name the name of the sequence - * @return mixed DB_OK on success or DB error on error - * @access public + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_ibase::nextID(), DB_ibase::createSequence() */ function dropSequence($seq_name) { - $sqn = strtoupper($this->getSequenceName($seq_name)); return $this->query('DELETE FROM RDB$GENERATORS ' - . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); + . "WHERE RDB\$GENERATOR_NAME='" + . strtoupper($this->getSequenceName($seq_name)) + . "'"); } // }}} // {{{ _ibaseFieldFlags() /** - * get the Flags of a Field + * Get the column's flags + * + * Supports "primary_key", "unique_key", "not_null", "default", + * "computed" and "blob". + * + * @param string $field_name the name of the field + * @param string $table_name the name of the table * - * @param string $field_name the name of the field - * @param string $table_name the name of the table + * @return string the flags * - * @return string The flags of the field ("primary_key", "unique_key", "not_null" - * "default", "computed" and "blob" are supported) * @access private */ function _ibaseFieldFlags($field_name, $table_name) @@ -614,47 +846,153 @@ class DB_ibase extends DB_common } // }}} + // {{{ ibaseRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_ibase::errorNative(), DB_ibase::errorCode() + */ + function &ibaseRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode($this->errorNative()); + } + $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg()); + return $tmp; + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code. NULL if there is no error code. + * + * @since Method available since Release 1.7.0 + */ + function errorNative() + { + if (function_exists('ibase_errcode')) { + return @ibase_errcode(); + } + if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i', + @ibase_errmsg(), $m)) { + return (int)$m[1]; + } + return null; + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones + * + * @param int $nativecode the error code returned by the DBMS + * + * @return int the portable DB error code. Return DB_ERROR if the + * current driver doesn't have a mapping for the + * $nativecode submitted. + * + * @since Method available since Release 1.7.0 + */ + function errorCode($nativecode = null) + { + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } + + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/generator .* is not defined/' + => DB_ERROR_SYNTAX, // for compat. w ibase_errcode() + '/table.*(not exist|not found|unknown)/i' + => DB_ERROR_NOSUCHTABLE, + '/table .* already exists/i' + => DB_ERROR_ALREADY_EXISTS, + '/unsuccessful metadata update .* failed attempt to store duplicate value/i' + => DB_ERROR_ALREADY_EXISTS, + '/unsuccessful metadata update .* not found/i' + => DB_ERROR_NOT_FOUND, + '/validation error for column .* value "\*\*\* null/i' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/violation of [\w ]+ constraint/i' + => DB_ERROR_CONSTRAINT, + '/conversion error from string/i' + => DB_ERROR_INVALID_NUMBER, + '/no permission for/i' + => DB_ERROR_ACCESS_VIOLATION, + '/arithmetic exception, numeric overflow, or string truncation/i' + => DB_ERROR_INVALID, + '/feature is not supported/i' + => DB_ERROR_NOT_CAPABLE, + ); + } + + $errormsg = @ibase_errmsg(); + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { - if (isset($result->result)) { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ibase_query($this->connection, + "SELECT * FROM $result WHERE 1=0"); + $got_string = true; + } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; - } elseif (is_string($result)) { - /* - * Probably received a table name. - * Create a result resource identifier. - */ - $id = @ibase_query($this->connection, - "SELECT * FROM $result WHERE 1=0"); - $got_string = true; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ - $id = $result; + $id = $result; $got_string = false; } @@ -669,34 +1007,28 @@ class DB_ibase extends DB_common } $count = @ibase_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - for ($i=0; $i<$count; $i++) { - $info = @ibase_field_info($id, $i); - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func($info['name']); - $res[$i]['type'] = $info['type']; - $res[$i]['len'] = $info['length']; - $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; + for ($i = 0; $i < $count; $i++) { + $info = @ibase_field_info($id, $i); + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func($info['name']), + 'type' => $info['type'], + 'len' => $info['length'], + 'flags' => ($got_string) + ? $this->_ibaseFieldFlags($info['name'], $result) + : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; } - } else { // full - $res['num_fields']= $count; - - for ($i=0; $i<$count; $i++) { - $info = @ibase_field_info($id, $i); - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func($info['name']); - $res[$i]['type'] = $info['type']; - $res[$i]['len'] = $info['length']; - $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; - - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -708,66 +1040,32 @@ class DB_ibase extends DB_common } // }}} - // {{{ ibaseRaiseError() + // {{{ getSpecialQuery() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve * - * @param integer $db_errno PEAR error number (usually a DB constant) if - * manually raising an error - * @param string $native_errmsg text of error message if known - * @return object DB error object - * @see DB_common::errorCode() - * @see DB_common::raiseError() + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() */ - function &ibaseRaiseError($db_errno = null, $native_errmsg = null) + function getSpecialQuery($type) { - if ($native_errmsg === null) { - $native_errmsg = @ibase_errmsg(); - } - // memo for the interbase php module hackers: we need something similar - // to mysql_errno() to retrieve error codes instead of this ugly hack - if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_errmsg, $m)) { - $native_errno = (int)$m[2]; - } else { - $native_errno = null; - } - // try to map the native error to the DB one - if ($db_errno === null) { - if ($native_errno) { - // try to interpret Interbase error code (that's why we need ibase_errno() - // in the interbase module to return the real error code) - switch ($native_errno) { - case -204: - if (is_int(strpos($m[3], 'Table unknown'))) { - $db_errno = DB_ERROR_NOSUCHTABLE; - } - break; - default: - $db_errno = $this->errorCode($native_errno); - } - } else { - $error_regexps = array( - '/[tT]able not found/' => DB_ERROR_NOSUCHTABLE, - '/[tT]able .* already exists/' => DB_ERROR_ALREADY_EXISTS, - '/validation error for column .* value "\*\*\* null/' => DB_ERROR_CONSTRAINT_NOT_NULL, - '/violation of [\w ]+ constraint/' => DB_ERROR_CONSTRAINT, - '/conversion error from string/' => DB_ERROR_INVALID_NUMBER, - '/no permission for/' => DB_ERROR_ACCESS_VIOLATION, - '/arithmetic exception, numeric overflow, or string truncation/' => DB_ERROR_DIVZERO - ); - foreach ($error_regexps as $regexp => $code) { - if (preg_match($regexp, $native_errmsg)) { - $db_errno = $code; - $native_errno = null; - break; - } - } - } + switch ($type) { + case 'tables': + return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM ' + . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0'; + case 'views': + return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS'; + case 'users': + return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'; + default: + return null; } - $tmp =& $this->raiseError($db_errno, null, null, null, $native_errmsg); - return $tmp; } // }}} diff --git a/thirdparty/pear/DB/ifx.php b/thirdparty/pear/DB/ifx.php old mode 100644 new mode 100755 index d317a3a..98ebecb --- a/thirdparty/pear/DB/ifx.php +++ b/thirdparty/pear/DB/ifx.php @@ -1,124 +1,217 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// Legend: -// For more info on Informix errors see: -// http://www.informix.com/answers/english/ierrors.htm -// -// TODO: -// - set needed env Informix vars on connect -// - implement native prepare/execute +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's ifx extension + * for interacting with Informix databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: ifx.php,v 1.75 2007/07/06 05:19:21 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's Informix - * extension. + * The methods PEAR DB uses to interact with PHP's ifx extension + * for interacting with Informix databases + * + * These methods overload the ones declared in DB_common. + * + * More info on Informix errors can be found at: + * http://www.informix.com/answers/english/ierrors.htm * - * @package DB - * @version $Id$ - * @category Database - * @author Tomas V.V.Cox + * TODO: + * - set needed env Informix vars on connect + * - implement native prepare/execute + * + * @category Database + * @package DB + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_ifx extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'ifx'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'ifx'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => 'emulate', + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + '-201' => DB_ERROR_SYNTAX, + '-206' => DB_ERROR_NOSUCHTABLE, + '-217' => DB_ERROR_NOSUCHFIELD, + '-236' => DB_ERROR_VALUE_COUNT_ON_ROW, + '-239' => DB_ERROR_CONSTRAINT, + '-253' => DB_ERROR_SYNTAX, + '-268' => DB_ERROR_CONSTRAINT, + '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-310' => DB_ERROR_ALREADY_EXISTS, + '-316' => DB_ERROR_ALREADY_EXISTS, + '-319' => DB_ERROR_NOT_FOUND, + '-329' => DB_ERROR_NODBSELECTED, + '-346' => DB_ERROR_CONSTRAINT, + '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-554' => DB_ERROR_SYNTAX, + '-691' => DB_ERROR_CONSTRAINT, + '-692' => DB_ERROR_CONSTRAINT, + '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-1202' => DB_ERROR_DIVZERO, + '-1204' => DB_ERROR_INVALID_DATE, + '-1205' => DB_ERROR_INVALID_DATE, + '-1206' => DB_ERROR_INVALID_DATE, + '-1209' => DB_ERROR_INVALID_DATE, + '-1210' => DB_ERROR_INVALID_DATE, + '-1212' => DB_ERROR_INVALID_DATE, + '-1213' => DB_ERROR_INVALID_NUMBER, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $affected = 0; + + /** + * The DSN information for connecting to a database + * @var array + */ var $dsn = array(); - var $transaction_opcount = 0; + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ var $autocommit = true; - var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_ifx() { - $this->phptype = 'ifx'; - $this->dbsyntax = 'ifx'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => true, - 'limit' => 'emulate' - ); - $this->errorcode_map = array( - '-201' => DB_ERROR_SYNTAX, - '-206' => DB_ERROR_NOSUCHTABLE, - '-217' => DB_ERROR_NOSUCHFIELD, - '-239' => DB_ERROR_CONSTRAINT, - '-253' => DB_ERROR_SYNTAX, - '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, - '-310' => DB_ERROR_ALREADY_EXISTS, - '-329' => DB_ERROR_NODBSELECTED, - '-346' => DB_ERROR_CONSTRAINT, - '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, - '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, - '-554' => DB_ERROR_SYNTAX, - '-691' => DB_ERROR_CONSTRAINT, - '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, - '-1204' => DB_ERROR_INVALID_DATE, - '-1205' => DB_ERROR_INVALID_DATE, - '-1206' => DB_ERROR_INVALID_DATE, - '-1209' => DB_ERROR_INVALID_DATE, - '-1210' => DB_ERROR_INVALID_DATE, - '-1212' => DB_ERROR_INVALID_DATE, - '-1213' => DB_ERROR_INVALID_NUMBER, - ); + $this->DB_common(); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent + * Don't call this method directly. Use DB::connect() instead. * - * @return int DB_OK on success, a DB error code on failure + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('informix') && - !DB::assertExtension('Informix')) + if (!PEAR::loadExtension('informix') && + !PEAR::loadExtension('Informix')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $dbhost = $dsninfo['hostspec'] ? '@' . $dsninfo['hostspec'] : ''; - $dbname = $dsninfo['database'] ? $dsninfo['database'] . $dbhost : ''; - $user = $dsninfo['username'] ? $dsninfo['username'] : ''; - $pw = $dsninfo['password'] ? $dsninfo['password'] : ''; + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : ''; + $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : ''; + $user = $dsn['username'] ? $dsn['username'] : ''; + $pw = $dsn['password'] ? $dsn['password'] : ''; $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; $this->connection = @$connect_function($dbname, $user, $pw); if (!is_resource($this->connection)) { - return $this->ifxraiseError(DB_ERROR_CONNECT_FAILED); + return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED); } return DB_OK; } @@ -127,9 +220,9 @@ class DB_ifx extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. + * Disconnects from the database server * - * @return bool true on success, false if not connected. + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -142,21 +235,20 @@ class DB_ifx extends DB_common // {{{ simpleQuery() /** - * Send a query to Informix and return the results as a - * Informix resource identifier. + * Sends a query to the database server * - * @param $query the SQL query + * @param string the SQL query string * - * @return int returns a valid Informix result for successful SELECT - * queries, DB_OK for other successful queries. A DB error code - * is returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; $this->affected = null; - if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()? + if (preg_match('/(SELECT|EXECUTE)/i', $query)) { //TESTME: Use !DB::isManip()? // the scroll is needed for fetching absolute row numbers // in a select query result $result = @ifx_query($query, $this->connection, IFX_SCROLL); @@ -165,7 +257,7 @@ class DB_ifx extends DB_common if ($this->transaction_opcount == 0) { $result = @ifx_query('BEGIN WORK', $this->connection); if (!$result) { - return $this->ifxraiseError(); + return $this->ifxRaiseError(); } } $this->transaction_opcount++; @@ -173,12 +265,12 @@ class DB_ifx extends DB_common $result = @ifx_query($query, $this->connection); } if (!$result) { - return $this->ifxraiseError(); + return $this->ifxRaiseError(); } $this->affected = @ifx_affected_rows($result); // Determine which queries should return data, and which // should return an error code only. - if (preg_match('/(SELECT)/i', $query)) { + if (preg_match('/(SELECT|EXECUTE)/i', $query)) { return $result; } // XXX Testme: free results inside a transaction @@ -211,43 +303,45 @@ class DB_ifx extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the last query. - * if the last query was a select, returns 0. + * Determines the number of rows affected by a data maniuplation query * - * @return number of rows affected by the last query + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (DB::isManip($this->last_query)) { + if ($this->_last_query_manip) { return $this->affected; } else { return 0; } - } // }}} // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if (($rownum !== null) && ($rownum < 0)) { return null; @@ -287,27 +381,25 @@ class DB_ifx extends DB_common } // }}} - // {{{ numRows() - - function numRows($result) - { - return $this->raiseError(DB_ERROR_NOT_CAPABLE); - } - - // }}} // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource * - * @param $result Informix result identifier + * @return int the number of columns. A DB_Error object on failure. * - * @return int the number of columns per row in $result + * @see DB_result::numCols() */ function numCols($result) { if (!$cols = @ifx_num_fields($result)) { - return $this->ifxraiseError(); + return $this->ifxRaiseError(); } return $cols; } @@ -316,22 +408,33 @@ class DB_ifx extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set * - * @param $result Informix result identifier + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @return bool true on success, false if $result is invalid + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() */ function freeResult($result) { - return @ifx_free_result($result); + return is_resource($result) ? ifx_free_result($result) : false; } // }}} // {{{ autoCommit() /** - * Enable/disable automatic commits + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = true) { @@ -345,7 +448,9 @@ class DB_ifx extends DB_common // {{{ commit() /** - * Commit the current transaction. + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @@ -363,7 +468,9 @@ class DB_ifx extends DB_common // {{{ rollback() /** - * Roll back (undo) the current transaction. + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @@ -378,34 +485,47 @@ class DB_ifx extends DB_common } // }}} - // {{{ ifxraiseError() + // {{{ ifxRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. - * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see errorNative() - * @see errorCode() - * @see DB_common::raiseError() + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_ifx::errorNative(), DB_ifx::errorCode() */ - function ifxraiseError($errno = null) + function ifxRaiseError($errno = null) { if ($errno === null) { $errno = $this->errorCode(ifx_error()); } - return $this->raiseError($errno, null, null, null, - $this->errorNative()); + $this->errorNative()); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code and message produced by the last query + * + * @return string the DBMS' error code and message + */ + function errorNative() + { + return @ifx_error() . ' ' . @ifx_errormsg(); } // }}} // {{{ errorCode() /** - * Map native error codes to DB's portable ones. + * Maps native error codes to DB's portable ones. * * Requires that the DB implementation's constructor fills * in the $errorcode_map property. @@ -426,42 +546,10 @@ class DB_ifx extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error message of the last error (if any) that - * occured on the current connection. - * - * @return int native Informix error code - */ - function errorNative() - { - return @ifx_error() . ' ' . @ifx_errormsg(); - } - - // }}} - // {{{ getSpecialQuery() - - /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string - */ - function getSpecialQuery($type) - { - switch ($type) { - case 'tables': - return 'select tabname from systables where tabid >= 100'; - default: - return null; - } - } - - // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * NOTE: only supports 'table' if $result is a table name. * @@ -470,25 +558,21 @@ class DB_ifx extends DB_common * can't distinguish duplicate field names. * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal - * @since 1.6.0 + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() + * @since Method available since Release 1.6.0 */ function tableInfo($result, $mode = null) { - if (isset($result->result)) { - /* - * Probably received a result object. - * Extract the result resource identifier. - */ - $id = $result->result; - $got_string = false; - } elseif (is_string($result)) { + if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. @@ -496,6 +580,13 @@ class DB_ifx extends DB_common $id = @ifx_query("SELECT * FROM $result WHERE 1=0", $this->connection); $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; } else { /* * Probably received a result resource identifier. @@ -522,40 +613,29 @@ class DB_ifx extends DB_common $case_func = 'strval'; } - $i = 0; - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - foreach ($flds as $key => $value) { - $props = explode(';', $value); - - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func($key); - $res[$i]['type'] = $props[0]; - $res[$i]['len'] = $props[1]; - $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : ''; - $i++; - } + $i = 0; + $res = array(); - } else { // full + if ($mode) { $res['num_fields'] = $count; + } - foreach ($flds as $key => $value) { - $props = explode(';', $value); - - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func($key); - $res[$i]['type'] = $props[0]; - $res[$i]['len'] = $props[1]; - $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : ''; - - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } - $i++; + foreach ($flds as $key => $value) { + $props = explode(';', $value); + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func($key), + 'type' => $props[0], + 'len' => $props[1], + 'flags' => $props[4] == 'N' ? 'not_null' : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } + $i++; } // free the result only if we were called on a table @@ -566,6 +646,30 @@ class DB_ifx extends DB_common } // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT tabname FROM systables WHERE tabid >= 100'; + default: + return null; + } + } + + // }}} } diff --git a/thirdparty/pear/DB/msql.php b/thirdparty/pear/DB/msql.php old mode 100644 new mode 100755 index c135b32..f9c107d --- a/thirdparty/pear/DB/msql.php +++ b/thirdparty/pear/DB/msql.php @@ -1,95 +1,227 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's msql extension + * for interacting with Mini SQL databases + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: msql.php,v 1.64 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's Mini-SQL - * extension. + * The methods PEAR DB uses to interact with PHP's msql extension + * for interacting with Mini SQL databases + * + * These methods overload the ones declared in DB_common. + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. * - * @package DB - * @version $Id$ - * @category Database - * @author Sterling Hughes + * @category Database + * @package DB + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB + * @since Class not functional until Release 1.7.0 */ class DB_msql extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'msql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'msql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The query result resource created by PHP + * + * Used to make affectedRows() work. Only contains the result for + * data manipulation queries. Contains false for other queries. + * + * @var resource + * @access private + */ + var $_result; + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_msql() { $this->DB_common(); - $this->phptype = 'msql'; - $this->dbsyntax = 'msql'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => false, - 'limit' => 'emulate' - ); } // }}} // {{{ connect() - function connect($dsninfo, $persistent = false) + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * Example of how to connect: + * + * require_once 'DB.php'; + * + * // $dsn = 'msql://hostname/dbname'; // use a TCP connection + * $dsn = 'msql:///dbname'; // use a socket + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('msql')) { + if (!PEAR::loadExtension('msql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array(); + if ($dsn['hostspec']) { + $params[] = $dsn['port'] + ? $dsn['hostspec'] . ',' . $dsn['port'] + : $dsn['hostspec']; + } $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect'; - if ($dbhost && $dsninfo['username'] && $dsninfo['password']) { - $conn = $connect_function($dbhost, $dsninfo['username'], - $dsninfo['password']); - } elseif ($dbhost && $dsninfo['username']) { - $conn = $connect_function($dbhost, $dsninfo['username']); + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); } else { - $conn = $connect_function($dbhost); + @ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + @ini_set('track_errors', $ini); } - if (!$conn) { - $this->raiseError(DB_ERROR_CONNECT_FAILED); + + if (!$this->connection) { + if (($err = @msql_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } } - if (!@msql_select_db($dsninfo['database'], $conn)){ - return $this->raiseError(DB_ERROR_NODBSELECTED); + + if (!@msql_select_db($dsn['database'], $this->connection)) { + return $this->msqlRaiseError(); } - $this->connection = $conn; return DB_OK; } // }}} // {{{ disconnect() + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ function disconnect() { $ret = @msql_close($this->connection); @@ -100,17 +232,32 @@ class DB_msql extends DB_common // }}} // {{{ simpleQuery() + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ function simpleQuery($query) { $this->last_query = $query; $query = $this->modifyQuery($query); $result = @msql_query($query, $this->connection); if (!$result) { - return $this->raiseError(); + return $this->msqlRaiseError(); } // Determine which queries that should return data, and which // should return an error code only. - return DB::isManip($query) ? DB_OK : $result; + if ($this->_checkManip($query)) { + $this->_result = $result; + return DB_OK; + } else { + $this->_result = false; + return $result; + } } @@ -135,24 +282,30 @@ class DB_msql extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@msql_data_seek($result, $rownum)) { @@ -168,11 +321,7 @@ class DB_msql extends DB_common $arr = @msql_fetch_row($result); } if (!$arr) { - if ($error = @msql_error()) { - return $this->raiseError($error); - } else { - return null; - } + return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); @@ -186,19 +335,45 @@ class DB_msql extends DB_common // }}} // {{{ freeResult() + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ function freeResult($result) { - return @msql_free_result($result); + return is_resource($result) ? msql_free_result($result) : false; } // }}} // {{{ numCols() + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ function numCols($result) { $cols = @msql_num_fields($result); if (!$cols) { - return $this->raiseError(); + return $this->msqlRaiseError(); } return $cols; } @@ -206,11 +381,24 @@ class DB_msql extends DB_common // }}} // {{{ numRows() + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ function numRows($result) { $rows = @msql_num_rows($result); - if (!$rows) { - return $this->raiseError(); + if ($rows === false) { + return $this->msqlRaiseError(); } return $rows; } @@ -219,13 +407,414 @@ class DB_msql extends DB_common // {{{ affected() /** - * Gets the number of rows affected by a query. + * Determines the number of rows affected by a data maniuplation query * - * @return number of rows affected by the last query + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - return @msql_affected_rows($this->connection); + if (!$this->_result) { + return 0; + } + return msql_affected_rows($this->_result); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_msql::createSequence(), DB_msql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = false; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("SELECT _seq FROM ${seqname}"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = true; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * Also creates a new table to associate the sequence with. Uses + * a separate table to ensure portability with other drivers. + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_msql::nextID(), DB_msql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER NOT NULL)'); + if (DB::isError($res)) { + return $res; + } + $res = $this->query("CREATE SEQUENCE ON ${seqname}"); + return $res; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_msql::nextID(), DB_msql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ quoteIdentifier() + + /** + * mSQL does not support delimited identifiers + * + * @param string $str the identifier name to be quoted + * + * @return object a DB_Error object + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.7.0 + */ + function quoteIdentifier($str) + { + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } + + // }}} + // {{{ quoteFloat() + + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.7.0 + */ + function escapeSimple($str) + { + return addslashes($str); + } + + // }}} + // {{{ msqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_msql::errorNative(), DB_msql::errorCode() + */ + function msqlRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @msql_error(); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message + * + * @param string $errormsg the error message returned from the database + * + * @return integer the error number from a DB_ERROR* constant + */ + function errorCode($errormsg) + { + static $error_regexps; + + // PHP 5.2+ prepends the function name to $php_errormsg, so we need + // this hack to work around it, per bug #9599. + $errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg); + + if (!isset($error_regexps)) { + $error_regexps = array( + '/^Access to database denied/i' + => DB_ERROR_ACCESS_VIOLATION, + '/^Bad index name/i' + => DB_ERROR_ALREADY_EXISTS, + '/^Bad order field/i' + => DB_ERROR_SYNTAX, + '/^Bad type for comparison/i' + => DB_ERROR_SYNTAX, + '/^Can\'t perform LIKE on/i' + => DB_ERROR_SYNTAX, + '/^Can\'t use TEXT fields in LIKE comparison/i' + => DB_ERROR_SYNTAX, + '/^Couldn\'t create temporary table/i' + => DB_ERROR_CANNOT_CREATE, + '/^Error creating table file/i' + => DB_ERROR_CANNOT_CREATE, + '/^Field .* cannot be null$/i' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^Index (field|condition) .* cannot be null$/i' + => DB_ERROR_SYNTAX, + '/^Invalid date format/i' + => DB_ERROR_INVALID_DATE, + '/^Invalid time format/i' + => DB_ERROR_INVALID, + '/^Literal value for .* is wrong type$/i' + => DB_ERROR_INVALID_NUMBER, + '/^No Database Selected/i' + => DB_ERROR_NODBSELECTED, + '/^No value specified for field/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + '/^Non unique value for unique index/i' + => DB_ERROR_CONSTRAINT, + '/^Out of memory for temporary table/i' + => DB_ERROR_CANNOT_CREATE, + '/^Permission denied/i' + => DB_ERROR_ACCESS_VIOLATION, + '/^Reference to un-selected table/i' + => DB_ERROR_SYNTAX, + '/^syntax error/i' + => DB_ERROR_SYNTAX, + '/^Table .* exists$/i' + => DB_ERROR_ALREADY_EXISTS, + '/^Unknown database/i' + => DB_ERROR_NOSUCHDB, + '/^Unknown field/i' + => DB_ERROR_NOSUCHFIELD, + '/^Unknown (index|system variable)/i' + => DB_ERROR_NOT_FOUND, + '/^Unknown table/i' + => DB_ERROR_NOSUCHTABLE, + '/^Unqualified field/i' + => DB_ERROR_SYNTAX, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::setOption() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @msql_query("SELECT * FROM $result", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @msql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $tmp = @msql_fetch_field($id); + + $flags = ''; + if ($tmp->not_null) { + $flags .= 'not_null '; + } + if ($tmp->unique) { + $flags .= 'unique_key '; + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($tmp->table), + 'name' => $case_func($tmp->name), + 'type' => $tmp->type, + 'len' => msql_field_len($id, $i), + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @msql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtain a list of a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return array the array containing the list of objects requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'databases': + $id = @msql_list_dbs($this->connection); + break; + case 'tables': + $id = @msql_list_tables($this->dsn['database'], + $this->connection); + break; + default: + return null; + } + if (!$id) { + return $this->msqlRaiseError(); + } + $out = array(); + while ($row = @msql_fetch_row($id)) { + $out[] = $row[0]; + } + return $out; } // }}} diff --git a/thirdparty/pear/DB/mssql.php b/thirdparty/pear/DB/mssql.php old mode 100644 new mode 100755 index 5bb843a..57e19b0 --- a/thirdparty/pear/DB/mssql.php +++ b/thirdparty/pear/DB/mssql.php @@ -1,118 +1,257 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's mssql extension + * for interacting with Microsoft SQL Server databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: mssql.php,v 1.92 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's Microsoft SQL Server - * extension. + * The methods PEAR DB uses to interact with PHP's mssql extension + * for interacting with Microsoft SQL Server databases + * + * These methods overload the ones declared in DB_common. + * + * DB's mssql driver is only for Microsfoft SQL Server databases. * - * @package DB - * @version $Id$ - * @category Database - * @author Sterling Hughes + * If you're connecting to a Sybase database, you MUST specify "sybase" + * as the "phptype" in the DSN. + * + * This class only works correctly if you have compiled PHP using + * --with-mssql=[dir_to_FreeTDS]. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_mssql extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'mssql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'mssql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX + var $errorcode_map = array( + 102 => DB_ERROR_SYNTAX, + 110 => DB_ERROR_VALUE_COUNT_ON_ROW, + 155 => DB_ERROR_NOSUCHFIELD, + 156 => DB_ERROR_SYNTAX, + 170 => DB_ERROR_SYNTAX, + 207 => DB_ERROR_NOSUCHFIELD, + 208 => DB_ERROR_NOSUCHTABLE, + 245 => DB_ERROR_INVALID_NUMBER, + 319 => DB_ERROR_SYNTAX, + 321 => DB_ERROR_NOSUCHFIELD, + 325 => DB_ERROR_SYNTAX, + 336 => DB_ERROR_SYNTAX, + 515 => DB_ERROR_CONSTRAINT_NOT_NULL, + 547 => DB_ERROR_CONSTRAINT, + 1018 => DB_ERROR_SYNTAX, + 1035 => DB_ERROR_SYNTAX, + 1913 => DB_ERROR_ALREADY_EXISTS, + 2209 => DB_ERROR_SYNTAX, + 2223 => DB_ERROR_SYNTAX, + 2248 => DB_ERROR_SYNTAX, + 2256 => DB_ERROR_SYNTAX, + 2257 => DB_ERROR_SYNTAX, + 2627 => DB_ERROR_CONSTRAINT, + 2714 => DB_ERROR_ALREADY_EXISTS, + 3607 => DB_ERROR_DIVZERO, + 3701 => DB_ERROR_NOSUCHTABLE, + 7630 => DB_ERROR_SYNTAX, + 8134 => DB_ERROR_DIVZERO, + 9303 => DB_ERROR_SYNTAX, + 9317 => DB_ERROR_SYNTAX, + 9318 => DB_ERROR_SYNTAX, + 9331 => DB_ERROR_SYNTAX, + 9332 => DB_ERROR_SYNTAX, + 15253 => DB_ERROR_SYNTAX, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); - var $transaction_opcount = 0; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ var $_db = null; + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_mssql() { $this->DB_common(); - $this->phptype = 'mssql'; - $this->dbsyntax = 'mssql'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => true, - 'limit' => 'emulate' - ); - // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX - $this->errorcode_map = array( - 170 => DB_ERROR_SYNTAX, - 207 => DB_ERROR_NOSUCHFIELD, - 208 => DB_ERROR_NOSUCHTABLE, - 245 => DB_ERROR_INVALID_NUMBER, - 515 => DB_ERROR_CONSTRAINT_NOT_NULL, - 547 => DB_ERROR_CONSTRAINT, - 2627 => DB_ERROR_CONSTRAINT, - 2714 => DB_ERROR_ALREADY_EXISTS, - 3701 => DB_ERROR_NOSUCHTABLE, - 8134 => DB_ERROR_DIVZERO, - ); } // }}} // {{{ connect() - function connect($dsninfo, $persistent = false) + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('mssql') && !DB::assertExtension('sybase') - && !DB::assertExtension('sybase_ct')) + if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase') + && !PEAR::loadExtension('sybase_ct')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; - $dbhost .= $dsninfo['port'] ? ',' . $dsninfo['port'] : ''; - $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } - if ($dbhost && $dsninfo['username'] && $dsninfo['password']) { - $conn = @$connect_function($dbhost, $dsninfo['username'], - $dsninfo['password']); - } elseif ($dbhost && $dsninfo['username']) { - $conn = @$connect_function($dbhost, $dsninfo['username']); - } else { - $conn = @$connect_function($dbhost); + $params = array( + $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + ); + if ($dsn['port']) { + $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':') + . $dsn['port']; } - if (!$conn) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, @mssql_get_last_message()); + + $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; + + $this->connection = @call_user_func_array($connect_function, $params); + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + @mssql_get_last_message()); } - if ($dsninfo['database']) { - if (!@mssql_select_db($dsninfo['database'], $conn)) { - return $this->raiseError(DB_ERROR_NODBSELECTED, null, null, - null, @mssql_get_last_message()); + if ($dsn['database']) { + if (!@mssql_select_db($dsn['database'], $this->connection)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + @mssql_get_last_message()); } - $this->_db = $dsninfo['database']; + $this->_db = $dsn['database']; } - $this->connection = $conn; return DB_OK; } // }}} // {{{ disconnect() + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ function disconnect() { $ret = @mssql_close($this->connection); @@ -123,9 +262,18 @@ class DB_mssql extends DB_common // }}} // {{{ simpleQuery() + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; if (!@mssql_select_db($this->_db, $this->connection)) { return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); @@ -170,24 +318,26 @@ class DB_mssql extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@mssql_data_seek($result, $rownum)) { @@ -195,7 +345,7 @@ class DB_mssql extends DB_common } } if ($fetchmode & DB_FETCHMODE_ASSOC) { - $arr = @mssql_fetch_array($result, MSSQL_ASSOC); + $arr = @mssql_fetch_assoc($result); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } @@ -203,12 +353,6 @@ class DB_mssql extends DB_common $arr = @mssql_fetch_row($result); } if (!$arr) { - /* This throws informative error messages, - don't use it for now - if ($msg = @mssql_get_last_message()) { - return $this->raiseError($msg); - } - */ return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { @@ -223,14 +367,40 @@ class DB_mssql extends DB_common // }}} // {{{ freeResult() + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ function freeResult($result) { - return @mssql_free_result($result); + return is_resource($result) ? mssql_free_result($result) : false; } // }}} // {{{ numCols() + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ function numCols($result) { $cols = @mssql_num_fields($result); @@ -243,6 +413,19 @@ class DB_mssql extends DB_common // }}} // {{{ numRows() + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ function numRows($result) { $rows = @mssql_num_rows($result); @@ -256,7 +439,12 @@ class DB_mssql extends DB_common // {{{ autoCommit() /** - * Enable/disable automatic commits + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { @@ -270,7 +458,9 @@ class DB_mssql extends DB_common // {{{ commit() /** - * Commit the current transaction. + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @@ -291,7 +481,9 @@ class DB_mssql extends DB_common // {{{ rollback() /** - * Roll back (undo) the current transaction. + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @@ -312,14 +504,15 @@ class DB_mssql extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the last query. - * if the last query was a select, returns 0. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return number of rows affected by the last query or DB_ERROR + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (DB::isManip($this->last_query)) { + if ($this->_last_query_manip) { $res = @mssql_query('select @@rowcount', $this->connection); if (!$res) { return $this->mssqlRaiseError(); @@ -345,13 +538,13 @@ class DB_mssql extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_mssql::createSequence(), DB_mssql::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -373,7 +566,15 @@ class DB_mssql extends DB_common return $this->raiseError($result); } } elseif (!DB::isError($result)) { - $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); + $result = $this->query("SELECT IDENT_CURRENT('$seqname')"); + if (DB::isError($result)) { + /* Fallback code for MS SQL Server 7.0, which doesn't have + * IDENT_CURRENT. This is *not* safe for concurrent + * requests, and really, if you're using it, you're in a + * world of hurt. Nevertheless, it's here to ensure BC. See + * bug #181 for the gory details.*/ + $result = $this->query("SELECT @@IDENTITY FROM $seqname"); + } $repeat = 0; } else { $repeat = false; @@ -391,19 +592,17 @@ class DB_mssql extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_mssql::nextID(), DB_mssql::dropSequence() */ function createSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("CREATE TABLE $seqname ". - '([id] [int] IDENTITY (1, 1) NOT NULL ,' . - '[vapor] [int] NULL)'); + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' ([id] [int] IDENTITY (1, 1) NOT NULL,' + . ' [vapor] [int] NULL)'); } // }}} @@ -414,25 +613,66 @@ class DB_mssql extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_mssql::nextID(), DB_mssql::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP TABLE $seqname"); + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quotes a string so it can be safely used as a table or column name + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 + */ + function quoteIdentifier($str) + { + return '[' . str_replace(']', ']]', $str) . ']'; + } + + // }}} + // {{{ mssqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_mssql::errorNative(), DB_mssql::errorCode() + */ + function mssqlRaiseError($code = null) + { + $message = @mssql_get_last_message(); + if (!$code) { + $code = $this->errorNative(); + } + return $this->raiseError($this->errorCode($code, $message), + null, null, null, "$code - $message"); } // }}} // {{{ errorNative() /** - * Determine MS SQL Server error code by querying @@ERROR. + * Gets the DBMS' native error code produced by the last query * - * @return mixed mssql's native error code or DB_ERROR if unknown. + * @return int the DBMS' error code */ function errorNative() { @@ -448,7 +688,7 @@ class DB_mssql extends DB_common // {{{ errorCode() /** - * Determine PEAR::DB error code from mssql's native codes. + * Determines PEAR::DB error code from mssql's native codes. * * If $nativecode isn't known yet, it will be looked up. * @@ -456,12 +696,17 @@ class DB_mssql extends DB_common * @return integer an error number from a DB error constant * @see errorNative() */ - function errorCode($nativecode = null) + function errorCode($nativecode = null, $msg = '') { if (!$nativecode) { $nativecode = $this->errorNative(); } if (isset($this->errorcode_map[$nativecode])) { + if ($nativecode == 3701 + && preg_match('/Cannot drop the index/i', $msg)) + { + return DB_ERROR_NOT_FOUND; + } return $this->errorcode_map[$nativecode]; } else { return DB_ERROR; @@ -469,57 +714,29 @@ class DB_mssql extends DB_common } // }}} - // {{{ mssqlRaiseError() - - /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. - * - * @param integer $code PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see errorCode() - * @see errorNative() - * @see DB_common::raiseError() - */ - function mssqlRaiseError($code = null) - { - $message = @mssql_get_last_message(); - if (!$code) { - $code = $this->errorNative(); - } - return $this->raiseError($this->errorCode($code), null, null, null, - "$code - $message"); - } - - // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { - if (isset($result->result)) { - /* - * Probably received a result object. - * Extract the result resource identifier. - */ - $id = $result->result; - $got_string = false; - } elseif (is_string($result)) { + if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. @@ -530,6 +747,13 @@ class DB_mssql extends DB_common $id = @mssql_query("SELECT * FROM $result WHERE 1=0", $this->connection); $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; } else { /* * Probably received a result resource identifier. @@ -551,35 +775,35 @@ class DB_mssql extends DB_common } $count = @mssql_num_fields($id); + $res = array(); - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func(@mssql_field_name($id, $i)); - $res[$i]['type'] = @mssql_field_type($id, $i); - $res[$i]['len'] = @mssql_field_length($id, $i); - // We only support flags for tables - $res[$i]['flags'] = $got_string ? $this->_mssql_field_flags($result, $res[$i]['name']) : ''; - } - - } else { // full - $res['num_fields']= $count; - - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func(@mssql_field_name($id, $i)); - $res[$i]['type'] = @mssql_field_type($id, $i); - $res[$i]['len'] = @mssql_field_length($id, $i); - // We only support flags for tables - $res[$i]['flags'] = $got_string ? $this->_mssql_field_flags($result, $res[$i]['name']) : ''; + if ($mode) { + $res['num_fields'] = $count; + } - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + for ($i = 0; $i < $count; $i++) { + if ($got_string) { + $flags = $this->_mssql_field_flags($result, + @mssql_field_name($id, $i)); + if (DB::isError($flags)) { + return $flags; } + } else { + $flags = ''; + } + + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func(@mssql_field_name($id, $i)), + 'type' => @mssql_field_type($id, $i), + 'len' => @mssql_field_length($id, $i), + 'flags' => $flags, + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -591,30 +815,12 @@ class DB_mssql extends DB_common } // }}} - // {{{ getSpecialQuery() - - /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string - */ - function getSpecialQuery($type) - { - switch ($type) { - case 'tables': - return "select name from sysobjects where type = 'U' order by name"; - case 'views': - return "select name from sysobjects where type = 'V'"; - default: - return null; - } - } - - // }}} // {{{ _mssql_field_flags() /** - * Get the flags for a field, currently supports "not_null", "primary_key", + * Get a column's flags + * + * Supports "not_null", "primary_key", * "auto_increment" (mssql identity), "timestamp" (mssql timestamp), * "unique_key" (mssql unique index, unique check or primary_key) and * "multiple_key" (multikey index) @@ -623,10 +829,13 @@ class DB_mssql extends DB_common * not useful at all - is the behaviour of mysql_field_flags that primary * keys are alway unique? is the interpretation of multiple_key correct? * - * @param string The table name - * @param string The field - * @author Joern Barthel + * @param string $table the table name + * @param string $column the field name + * + * @return string the flags + * * @access private + * @author Joern Barthel */ function _mssql_field_flags($table, $column) { @@ -639,7 +848,10 @@ class DB_mssql extends DB_common $tableName = $table; // get unique and primary keys - $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC); + $res = $this->getAll("EXEC SP_HELPINDEX $table", DB_FETCHMODE_ASSOC); + if (DB::isError($res)) { + return $res; + } foreach ($res as $val) { $keys = explode(', ', $val['index_keys']); @@ -662,7 +874,10 @@ class DB_mssql extends DB_common } // get auto_increment, not_null and timestamp - $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC); + $res = $this->getAll("EXEC SP_COLUMNS $table", DB_FETCHMODE_ASSOC); + if (DB::isError($res)) { + return $res; + } foreach ($res as $val) { $val = array_change_key_case($val, CASE_LOWER); @@ -689,10 +904,13 @@ class DB_mssql extends DB_common /** * Adds a string to the flags array if the flag is not yet in there - * - if there is no flag present the array is created. + * - if there is no flag present the array is created + * + * @param array &$array the reference to the flag-array + * @param string $value the flag value + * + * @return void * - * @param reference Reference to the flag-array - * @param value The flag value * @access private * @author Joern Barthel */ @@ -706,23 +924,30 @@ class DB_mssql extends DB_common } // }}} - // {{{ quoteIdentifier() + // {{{ getSpecialQuery() /** - * Quote a string so it can be safely used as a table / column name + * Obtains the query string needed for listing a given type of objects * - * Quoting style depends on which database driver is being used. + * @param string $type the kind of objects you want to retrieve * - * @param string $str identifier name to be quoted - * - * @return string quoted identifier string + * @return string the SQL query string or null if the driver doesn't + * support the object type requested * - * @since 1.6.0 - * @access public + * @access protected + * @see DB_common::getListOf() */ - function quoteIdentifier($str) + function getSpecialQuery($type) { - return '[' . str_replace(']', ']]', $str) . ']'; + switch ($type) { + case 'tables': + return "SELECT name FROM sysobjects WHERE type = 'U'" + . ' ORDER BY name'; + case 'views': + return "SELECT name FROM sysobjects WHERE type = 'V'"; + default: + return null; + } } // }}} diff --git a/thirdparty/pear/DB/mysql.php b/thirdparty/pear/DB/mysql.php old mode 100644 new mode 100755 index e3bc951..d7b8380 --- a/thirdparty/pear/DB/mysql.php +++ b/thirdparty/pear/DB/mysql.php @@ -1,173 +1,269 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// XXX legend: -// -// XXX ERRORMSG: The error message from the mysql function should -// be registered here. -// -// TODO/wishlist: -// longReadlen -// binmode +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's mysql extension + * for interacting with MySQL databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: mysql.php,v 1.126 2007/09/21 13:32:52 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's MySQL - * extension. + * The methods PEAR DB uses to interact with PHP's mysql extension + * for interacting with MySQL databases * - * This is for MySQL versions 4.0 and below. + * These methods overload the ones declared in DB_common. * - * @package DB - * @version $Id$ - * @category Database - * @author Stig Bakken + * @category Database + * @package DB + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_mysql extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'mysql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'mysql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '4.2.0', + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1022 => DB_ERROR_ALREADY_EXISTS, + 1044 => DB_ERROR_ACCESS_VIOLATION, + 1046 => DB_ERROR_NODBSELECTED, + 1048 => DB_ERROR_CONSTRAINT, + 1049 => DB_ERROR_NOSUCHDB, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1061 => DB_ERROR_ALREADY_EXISTS, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1091 => DB_ERROR_NOT_FOUND, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1142 => DB_ERROR_ACCESS_VIOLATION, + 1146 => DB_ERROR_NOSUCHTABLE, + 1216 => DB_ERROR_CONSTRAINT, + 1217 => DB_ERROR_CONSTRAINT, + 1356 => DB_ERROR_DIVZERO, + 1451 => DB_ERROR_CONSTRAINT, + 1452 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); - var $num_rows = array(); - var $transaction_opcount = 0; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ var $autocommit = true; - var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ - var $_db = false; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; + // }}} // {{{ constructor /** - * DB_mysql constructor. + * This constructor calls $this->DB_common() * - * @access public + * @return void */ function DB_mysql() { $this->DB_common(); - $this->phptype = 'mysql'; - $this->dbsyntax = 'mysql'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => true, - 'limit' => 'alter' - ); - $this->errorcode_map = array( - 1004 => DB_ERROR_CANNOT_CREATE, - 1005 => DB_ERROR_CANNOT_CREATE, - 1006 => DB_ERROR_CANNOT_CREATE, - 1007 => DB_ERROR_ALREADY_EXISTS, - 1008 => DB_ERROR_CANNOT_DROP, - 1022 => DB_ERROR_ALREADY_EXISTS, - 1046 => DB_ERROR_NODBSELECTED, - 1048 => DB_ERROR_CONSTRAINT, - 1050 => DB_ERROR_ALREADY_EXISTS, - 1051 => DB_ERROR_NOSUCHTABLE, - 1054 => DB_ERROR_NOSUCHFIELD, - 1062 => DB_ERROR_ALREADY_EXISTS, - 1064 => DB_ERROR_SYNTAX, - 1100 => DB_ERROR_NOT_LOCKED, - 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, - 1146 => DB_ERROR_NOSUCHTABLE, - 1216 => DB_ERROR_CONSTRAINT, - 1217 => DB_ERROR_CONSTRAINT, - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent - * @access public - * @return int DB_OK on success, a DB error on failure + * PEAR DB's mysql driver supports the following extra DSN options: + * + new_link If set to true, causes subsequent calls to connect() + * to return a new connection link instead of the + * existing one. WARNING: this is not portable to + * other DBMS's. Available since PEAR DB 1.7.0. + * + client_flags Any combination of MYSQL_CLIENT_* constants. + * Only used if PHP is at version 4.3.0 or greater. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('mysql')) { + if (!PEAR::loadExtension('mysql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') { - $dbhost = ':' . $dsninfo['socket']; + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array(); + if ($dsn['protocol'] && $dsn['protocol'] == 'unix') { + $params[0] = ':' . $dsn['socket']; } else { - $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; - if ($dsninfo['port']) { - $dbhost .= ':' . $dsninfo['port']; + $params[0] = $dsn['hostspec'] ? $dsn['hostspec'] + : 'localhost'; + if ($dsn['port']) { + $params[0] .= ':' . $dsn['port']; + } + } + $params[] = $dsn['username'] ? $dsn['username'] : null; + $params[] = $dsn['password'] ? $dsn['password'] : null; + + if (!$persistent) { + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + $params[] = true; + } else { + $params[] = false; } } + if (version_compare(phpversion(), '4.3.0', '>=')) { + $params[] = isset($dsn['client_flags']) + ? $dsn['client_flags'] : null; + } $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; - if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) { - $conn = @$connect_function($dbhost, $dsninfo['username'], - $dsninfo['password']); - } elseif ($dbhost && $dsninfo['username']) { - $conn = @$connect_function($dbhost, $dsninfo['username']); - } elseif ($dbhost) { - $conn = @$connect_function($dbhost); + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); } else { - $conn = false; + @ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + @ini_set('track_errors', $ini); } - if (!$conn) { + + if (!$this->connection) { if (($err = @mysql_error()) != '') { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $err); - } elseif (empty($php_errormsg)) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED); + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); } else { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $php_errormsg); + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); } } - if ($dsninfo['database']) { - if (!@mysql_select_db($dsninfo['database'], $conn)) { - switch(mysql_errno($conn)) { - case 1049: - return $this->raiseError(DB_ERROR_NOSUCHDB, null, null, - null, @mysql_error($conn)); - case 1044: - return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null, - null, @mysql_error($conn)); - default: - return $this->raiseError(DB_ERROR, null, null, - null, @mysql_error($conn)); - } + if ($dsn['database']) { + if (!@mysql_select_db($dsn['database'], $this->connection)) { + return $this->mysqlRaiseError(); } - // fix to allow calls to different databases in the same script - $this->_db = $dsninfo['database']; + $this->_db = $dsn['database']; } - $this->connection = $conn; return DB_OK; } @@ -175,11 +271,9 @@ class DB_mysql extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. - * - * @access public + * Disconnects from the database server * - * @return bool true on success, false if not connected. + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -192,20 +286,21 @@ class DB_mysql extends DB_common // {{{ simpleQuery() /** - * Send a query to MySQL and return the results as a MySQL resource - * identifier. + * Sends a query to the database server * - * @param the SQL query + * Generally uses mysql_query(). If you want to use + * mysql_unbuffered_query() set the "result_buffering" option to 0 using + * setOptions(). This option was added in Release 1.7.0. * - * @access public + * @param string the SQL query string * - * @return mixed returns a valid MySQL result for successful SELECT - * queries, DB_OK for other successful queries. A DB error is - * returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); if ($this->_db) { @@ -223,16 +318,15 @@ class DB_mysql extends DB_common } $this->transaction_opcount++; } - $result = @mysql_query($query, $this->connection); + if (!$this->options['result_buffering']) { + $result = @mysql_unbuffered_query($query, $this->connection); + } else { + $result = @mysql_query($query, $this->connection); + } if (!$result) { return $this->mysqlRaiseError(); } if (is_resource($result)) { - $numrows = $this->numrows($result); - if (is_object($numrows)) { - return $numrows; - } - $this->num_rows[(int)$result] = $numrows; return $result; } return DB_OK; @@ -248,8 +342,6 @@ class DB_mysql extends DB_common * * @param a valid sql result resource * - * @access public - * * @return false */ function nextResult($result) @@ -261,24 +353,26 @@ class DB_mysql extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@mysql_data_seek($result, $rownum)) { @@ -294,16 +388,7 @@ class DB_mysql extends DB_common $arr = @mysql_fetch_row($result); } if (!$arr) { - // See: http://bugs.php.net/bug.php?id=22328 - // for why we can't check errors on fetching return null; - /* - $errno = @mysql_errno($this->connection); - if (!$errno) { - return null; - } - return $this->mysqlRaiseError($errno); - */ } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { /* @@ -323,40 +408,45 @@ class DB_mysql extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @param $result MySQL result identifier + * @param resource $result PHP's query result resource * - * @access public + * @return bool TRUE on success, FALSE if $result is invalid * - * @return bool true on success, false if $result is invalid + * @see DB_result::free() */ function freeResult($result) { - unset($this->num_rows[(int)$result]); - return @mysql_free_result($result); + return is_resource($result) ? mysql_free_result($result) : false; } // }}} // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set * - * @param $result MySQL result identifier + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return int the number of columns per row in $result + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() */ function numCols($result) { $cols = @mysql_num_fields($result); - if (!$cols) { return $this->mysqlRaiseError(); } - return $cols; } @@ -364,13 +454,17 @@ class DB_mysql extends DB_common // {{{ numRows() /** - * Get the number of rows in a result set. + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @param $result MySQL result identifier + * @param resource $result PHP's query result resource * - * @access public + * @return int the number of rows. A DB_Error object on failure. * - * @return int the number of rows in $result + * @see DB_result::numRows() */ function numRows($result) { @@ -385,7 +479,12 @@ class DB_mysql extends DB_common // {{{ autoCommit() /** - * Enable/disable automatic commits + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { @@ -399,7 +498,9 @@ class DB_mysql extends DB_common // {{{ commit() /** - * Commit the current transaction. + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @@ -423,7 +524,9 @@ class DB_mysql extends DB_common // {{{ rollback() /** - * Roll back (undo) the current transaction. + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @@ -447,14 +550,15 @@ class DB_mysql extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the data manipulation - * query. For other queries, this function returns 0. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return number of rows affected by the last query + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (DB::isManip($this->last_query)) { + if ($this->_last_query_manip) { return @mysql_affected_rows($this->connection); } else { return 0; @@ -462,22 +566,6 @@ class DB_mysql extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @access public - * - * @return int native MySQL error code - */ - function errorNative() - { - return @mysql_errno($this->connection); - } - - // }}} // {{{ nextId() /** @@ -485,13 +573,13 @@ class DB_mysql extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_mysql::createSequence(), DB_mysql::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -503,20 +591,20 @@ class DB_mysql extends DB_common 'SET id=LAST_INSERT_ID(id+1)'); $this->popErrorHandling(); if ($result === DB_OK) { - /** COMMON CASE **/ + // COMMON CASE $id = @mysql_insert_id($this->connection); if ($id != 0) { return $id; } - /** EMPTY SEQ TABLE **/ - // Sequence table must be empty for some reason, so fill it and return 1 - // Obtain a user-level lock + // EMPTY SEQ TABLE + // Sequence table must be empty for some reason, so fill + // it and return 1 and obtain a user-level lock $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); if (DB::isError($result)) { return $this->raiseError($result); } if ($result == 0) { - // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error + // Failed to get the lock return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); } @@ -527,17 +615,18 @@ class DB_mysql extends DB_common } // Release the lock - $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + $result = $this->getOne('SELECT RELEASE_LOCK(' + . "'${seqname}_lock')"); if (DB::isError($result)) { return $this->raiseError($result); } // We know what the result will be, so no need to try again return 1; - /** ONDEMAND TABLE CREATION **/ } elseif ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { + // ONDEMAND TABLE CREATION $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $this->raiseError($result); @@ -545,10 +634,10 @@ class DB_mysql extends DB_common $repeat = 1; } - /** BACKWARDS COMPAT **/ } elseif (DB::isError($result) && $result->getCode() == DB_ERROR_ALREADY_EXISTS) { + // BACKWARDS COMPAT // see _BCsequence() comment $result = $this->_BCsequence($seqname); if (DB::isError($result)) { @@ -569,19 +658,17 @@ class DB_mysql extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_mysql::nextID(), DB_mysql::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); - $res = $this->query("CREATE TABLE ${seqname} ". - '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. - ' PRIMARY KEY(id))'); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' + . ' PRIMARY KEY(id))'); if (DB::isError($res)) { return $res; } @@ -591,7 +678,7 @@ class DB_mysql extends DB_common return $res; } // so reset to zero - return $this->query("UPDATE ${seqname} SET id = 0;"); + return $this->query("UPDATE ${seqname} SET id = 0"); } // }}} @@ -602,11 +689,10 @@ class DB_mysql extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_mysql::nextID(), DB_mysql::createSequence() */ function dropSequence($seq_name) { @@ -620,8 +706,11 @@ class DB_mysql extends DB_common * Backwards compatibility with old sequence emulation implementation * (clean up the dupes) * - * @param string $seqname The sequence name to clean up - * @return mixed DB_Error or true + * @param string $seqname the sequence name to clean up + * + * @return bool true on success. A DB_Error object on failure. + * + * @access private */ function _BCsequence($seqname) { @@ -645,7 +734,8 @@ class DB_mysql extends DB_common // This should kill all rows except the highest // We should probably do something if $highest_id isn't // numeric, but I'm at a loss as how to handle that... - $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id"); + $result = $this->query('DELETE FROM ' . $seqname + . " WHERE id <> $highest_id"); if (DB::isError($result)) { return $result; } @@ -664,24 +754,22 @@ class DB_mysql extends DB_common // {{{ quoteIdentifier() /** - * Quote a string so it can be safely used as a table or column name + * Quotes a string so it can be safely used as a table or column name + * (WARNING: using names that require this is a REALLY BAD IDEA) * - * Quoting style depends on which database driver is being used. - * - * MySQL can't handle the backtick character (`) in - * table or column names. + * WARNING: Older versions of MySQL can't handle the backtick + * character (`) in table or column names. * * @param string $str identifier name to be quoted * * @return string quoted identifier string * - * @since 1.6.0 - * @access public - * @internal + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { - return '`' . $str . '`'; + return '`' . str_replace('`', '``', $str) . '`'; } // }}} @@ -689,9 +777,9 @@ class DB_mysql extends DB_common /** * @deprecated Deprecated in release 1.6.0 - * @internal */ - function quote($str) { + function quote($str) + { return $this->quoteSmart($str); } @@ -699,15 +787,17 @@ class DB_mysql extends DB_common // {{{ escapeSimple() /** - * Escape a string according to the current DBMS's standards + * Escapes a string according to the current DBMS's standards * * @param string $str the string to be escaped * * @return string the escaped string * - * @internal + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 */ - function escapeSimple($str) { + function escapeSimple($str) + { if (function_exists('mysql_real_escape_string')) { return @mysql_real_escape_string($str, $this->connection); } else { @@ -718,6 +808,20 @@ class DB_mysql extends DB_common // }}} // {{{ modifyQuery() + /** + * Changes a query string for various DBMS specific reasons + * + * This little hack lets you know how many rows were deleted + * when running a "DELETE FROM table" query. Only implemented + * if the DB_PORTABILITY_DELETE_COUNT portability option is on. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + * @see DB_common::setOption() + */ function modifyQuery($query) { if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { @@ -734,9 +838,25 @@ class DB_mysql extends DB_common // }}} // {{{ modifyLimitQuery() + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ function modifyLimitQuery($query, $from, $count, $params = array()) { - if (DB::isManip($query)) { + if (DB::isManip($query) || $this->_next_query_manip) { return $query . " LIMIT $count"; } else { return $query . " LIMIT $from, $count"; @@ -747,14 +867,16 @@ class DB_mysql extends DB_common // {{{ mysqlRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see DB_common::errorCode() - * @see DB_common::raiseError() + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_mysql::errorNative(), DB_common::errorCode() */ function mysqlRaiseError($errno = null) { @@ -777,36 +899,60 @@ class DB_mysql extends DB_common } // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + return @mysql_errno($this->connection); + } + + // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() */ - function tableInfo($result, $mode = null) { - if (isset($result->result)) { + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + // Fix for bug #11580. + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @mysql_query("SELECT * FROM $result LIMIT 0", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; - } elseif (is_string($result)) { - /* - * Probably received a table name. - * Create a result resource identifier. - */ - $id = @mysql_list_fields($this->dsn['database'], - $result, $this->connection); - $got_string = true; } else { /* * Probably received a result resource identifier. @@ -828,32 +974,25 @@ class DB_mysql extends DB_common } $count = @mysql_num_fields($id); + $res = array(); - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $case_func(@mysql_field_table($id, $i)); - $res[$i]['name'] = $case_func(@mysql_field_name($id, $i)); - $res[$i]['type'] = @mysql_field_type($id, $i); - $res[$i]['len'] = @mysql_field_len($id, $i); - $res[$i]['flags'] = @mysql_field_flags($id, $i); + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $case_func(@mysql_field_table($id, $i)), + 'name' => $case_func(@mysql_field_name($id, $i)), + 'type' => @mysql_field_type($id, $i), + 'len' => @mysql_field_len($id, $i), + 'flags' => @mysql_field_flags($id, $i), + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; } - } else { // full - $res['num_fields']= $count; - - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $case_func(@mysql_field_table($id, $i)); - $res[$i]['name'] = $case_func(@mysql_field_name($id, $i)); - $res[$i]['type'] = @mysql_field_type($id, $i); - $res[$i]['len'] = @mysql_field_len($id, $i); - $res[$i]['flags'] = @mysql_field_flags($id, $i); - - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -868,33 +1007,23 @@ class DB_mysql extends DB_common // {{{ getSpecialQuery() /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SHOW TABLES'; - case 'views': - return DB_ERROR_NOT_CAPABLE; case 'users': - $sql = 'select distinct User from user'; - if ($this->dsn['database'] != 'mysql') { - $dsn = $this->dsn; - $dsn['database'] = 'mysql'; - if (DB::isError($db = DB::connect($dsn))) { - return $db; - } - $sql = $db->getCol($sql); - $db->disconnect(); - // XXX Fixme the mysql driver should take care of this - if (!@mysql_select_db($this->dsn['database'], $this->connection)) { - return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); - } - } - return $sql; + return 'SELECT DISTINCT User FROM mysql.user'; case 'databases': return 'SHOW DATABASES'; default: @@ -904,21 +1033,21 @@ class DB_mysql extends DB_common // }}} - // {{{ _getLastId() - /** + // {{{ getLastId() + /** * Returns the last insert ID * * @return int * * @access private */ - function _getLastId() - { - return mysql_insert_id($this->connection); - } + function getLastId() + { + return mysql_insert_id($this->connection); + } + // }}} - // }}} } diff --git a/thirdparty/pear/DB/mysqli.php b/thirdparty/pear/DB/mysqli.php old mode 100644 new mode 100755 index fa15b44..4449484 --- a/thirdparty/pear/DB/mysqli.php +++ b/thirdparty/pear/DB/mysqli.php @@ -1,56 +1,164 @@ | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// EXPERIMENTAL +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's mysqli extension + * for interacting with MySQL databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: mysqli.php,v 1.82 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's mysqli - * extension. + * The methods PEAR DB uses to interact with PHP's mysqli extension + * for interacting with MySQL databases * * This is for MySQL versions 4.1 and above. Requires PHP 5. * * Note that persistent connections no longer exist. * - * @package DB - * @version $Id$ - * @category Database - * @author Daniel Convissor - * @since Class functional since Release 1.6.3 + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB + * @since Class functional since Release 1.6.3 */ class DB_mysqli extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'mysqli'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'mysqli'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => false, + 'prepare' => false, + 'ssl' => true, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1022 => DB_ERROR_ALREADY_EXISTS, + 1044 => DB_ERROR_ACCESS_VIOLATION, + 1046 => DB_ERROR_NODBSELECTED, + 1048 => DB_ERROR_CONSTRAINT, + 1049 => DB_ERROR_NOSUCHDB, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1061 => DB_ERROR_ALREADY_EXISTS, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1091 => DB_ERROR_NOT_FOUND, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1142 => DB_ERROR_ACCESS_VIOLATION, + 1146 => DB_ERROR_NOSUCHTABLE, + 1216 => DB_ERROR_CONSTRAINT, + 1217 => DB_ERROR_CONSTRAINT, + 1356 => DB_ERROR_DIVZERO, + 1451 => DB_ERROR_CONSTRAINT, + 1452 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); - var $num_rows = array(); - var $transaction_opcount = 0; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ var $autocommit = true; - var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */ - var $_db = false; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; /** * Array for converting MYSQLI_*_FLAG constants to text values @@ -105,121 +213,140 @@ class DB_mysqli extends DB_common MYSQLI_TYPE_VAR_STRING => 'varchar', MYSQLI_TYPE_STRING => 'char', MYSQLI_TYPE_GEOMETRY => 'geometry', + /* These constants are conditionally compiled in ext/mysqli, so we'll + * define them by number rather than constant. */ + 16 => 'bit', + 246 => 'decimal', ); + // }}} // {{{ constructor /** - * DB_mysql constructor. + * This constructor calls $this->DB_common() * - * @access public + * @return void */ function DB_mysqli() { $this->DB_common(); - $this->phptype = 'mysqli'; - $this->dbsyntax = 'mysqli'; - $this->features = array( - 'prepare' => false, - 'ssl' => true, - 'transactions' => true, - 'limit' => 'alter' - ); - $this->errorcode_map = array( - 1004 => DB_ERROR_CANNOT_CREATE, - 1005 => DB_ERROR_CANNOT_CREATE, - 1006 => DB_ERROR_CANNOT_CREATE, - 1007 => DB_ERROR_ALREADY_EXISTS, - 1008 => DB_ERROR_CANNOT_DROP, - 1022 => DB_ERROR_ALREADY_EXISTS, - 1046 => DB_ERROR_NODBSELECTED, - 1048 => DB_ERROR_CONSTRAINT, - 1050 => DB_ERROR_ALREADY_EXISTS, - 1051 => DB_ERROR_NOSUCHTABLE, - 1054 => DB_ERROR_NOSUCHFIELD, - 1062 => DB_ERROR_ALREADY_EXISTS, - 1064 => DB_ERROR_SYNTAX, - 1100 => DB_ERROR_NOT_LOCKED, - 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, - 1146 => DB_ERROR_NOSUCHTABLE, - 1216 => DB_ERROR_CONSTRAINT, - 1217 => DB_ERROR_CONSTRAINT, - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database * - * @param string $dsn the data source name (see DB::parseDSN for syntax) - * @param boolean $persistent (optional) whether the connection should - * be persistent - * @return mixed DB_OK on success, a DB error on failure - * @access public + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's mysqli driver supports the following extra DSN options: + * + When the 'ssl' $option passed to DB::connect() is true: + * + key The path to the key file. + * + cert The path to the certificate file. + * + ca The path to the certificate authority file. + * + capath The path to a directory that contains trusted SSL + * CA certificates in pem format. + * + cipher The list of allowable ciphers for SSL encryption. + * + * Example of how to connect using SSL: + * + * require_once 'DB.php'; + * + * $dsn = array( + * 'phptype' => 'mysqli', + * 'username' => 'someuser', + * 'password' => 'apasswd', + * 'hostspec' => 'localhost', + * 'database' => 'thedb', + * 'key' => 'client-key.pem', + * 'cert' => 'client-cert.pem', + * 'ca' => 'cacert.pem', + * 'capath' => '/path/to/ca/dir', + * 'cipher' => 'AES', + * ); + * + * $options = array( + * 'ssl' => true, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('mysqli')) { + if (!PEAR::loadExtension('mysqli')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $conn = false; - @ini_set('track_errors', true); + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $ini = ini_get('track_errors'); + @ini_set('track_errors', 1); + $php_errormsg = ''; - if ($this->getOption('ssl') === true) { + if (((int) $this->getOption('ssl')) === 1) { $init = mysqli_init(); mysqli_ssl_set( $init, - empty($dsninfo['key']) ? null : $dsninfo['key'], - empty($dsninfo['cert']) ? null : $dsninfo['cert'], - empty($dsninfo['ca']) ? null : $dsninfo['ca'], - empty($dsninfo['capath']) ? null : $dsninfo['capath'], - empty($dsninfo['cipher']) ? null : $dsninfo['cipher'] + empty($dsn['key']) ? null : $dsn['key'], + empty($dsn['cert']) ? null : $dsn['cert'], + empty($dsn['ca']) ? null : $dsn['ca'], + empty($dsn['capath']) ? null : $dsn['capath'], + empty($dsn['cipher']) ? null : $dsn['cipher'] ); - if ($conn = @mysqli_real_connect($init, - $dsninfo['hostspec'], - $dsninfo['username'], - $dsninfo['password'], - $dsninfo['database'], - $dsninfo['port'], - $dsninfo['socket'])) + if ($this->connection = @mysqli_real_connect( + $init, + $dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['database'], + $dsn['port'], + $dsn['socket'])) { - $conn = $init; + $this->connection = $init; } } else { - $conn = @mysqli_connect( - $dsninfo['hostspec'], - $dsninfo['username'], - $dsninfo['password'], - $dsninfo['database'], - $dsninfo['port'], - $dsninfo['socket'] + $this->connection = @mysqli_connect( + $dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['database'], + $dsn['port'], + $dsn['socket'] ); } - @ini_restore('track_errors'); + @ini_set('track_errors', $ini); - if (!$conn) { + if (!$this->connection) { if (($err = @mysqli_connect_error()) != '') { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $err); - } elseif (empty($php_errormsg)) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED); + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); } else { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $php_errormsg); + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); } } - if ($dsninfo['database']) { - $this->_db = $dsninfo['database']; + if ($dsn['database']) { + $this->_db = $dsn['database']; } - $this->connection = $conn; return DB_OK; } @@ -227,10 +354,9 @@ class DB_mysqli extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. + * Disconnects from the database server * - * @return boolean true on success, false if not connected - * @access public + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -243,23 +369,22 @@ class DB_mysqli extends DB_common // {{{ simpleQuery() /** - * Send a query to MySQL and return the results as a MySQL resource - * identifier. + * Sends a query to the database server * - * @param string $query the SQL query - * @return mixed a valid MySQL result for successful SELECT - * queries, DB_OK for other successful queries. - * A DB error is returned on failure. - * @access public + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { - return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } if (!$this->autocommit && $ismanip) { @@ -267,24 +392,16 @@ class DB_mysqli extends DB_common $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0'); $result = @mysqli_query($this->connection, 'BEGIN'); if (!$result) { - return $this->mysqlRaiseError(); + return $this->mysqliRaiseError(); } } $this->transaction_opcount++; } $result = @mysqli_query($this->connection, $query); if (!$result) { - return $this->mysqlRaiseError(); + return $this->mysqliRaiseError(); } -# this next block is still sketchy.. if (is_object($result)) { - $numrows = $this->numrows($result); - if (is_object($numrows)) { - return $numrows; - } -# need to come up with different means for next line -# since $result is object (int)$result won't fly... -// $this->num_rows[(int)$result] = $numrows; return $result; } return DB_OK; @@ -311,24 +428,26 @@ class DB_mysqli extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@mysqli_data_seek($result, $rownum)) { @@ -344,11 +463,7 @@ class DB_mysqli extends DB_common $arr = @mysqli_fetch_row($result); } if (!$arr) { - $errno = @mysqli_errno($this->connection); - if (!$errno) { - return null; - } - return $this->mysqlRaiseError($errno); + return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { /* @@ -368,40 +483,45 @@ class DB_mysqli extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set * - * @param resource $result MySQL result identifier - * @return bool true on success, false if $result is invalid - * @access public + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() */ function freeResult($result) { -# need to come up with different means for next line -# since $result is object (int)$result won't fly... -// unset($this->num_rows[(int)$result]); - return @mysqli_free_result($result); + return is_resource($result) ? mysqli_free_result($result) : false; } // }}} // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set * - * @param $result MySQL result identifier + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return int the number of columns per row in $result + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() */ function numCols($result) { $cols = @mysqli_num_fields($result); - if (!$cols) { - return $this->mysqlRaiseError(); + return $this->mysqliRaiseError(); } - return $cols; } @@ -409,17 +529,23 @@ class DB_mysqli extends DB_common // {{{ numRows() /** - * Get the number of rows in a result set. + * Gets the number of rows in a result set * - * @param resource $result MySQL result identifier - * @return int the number of rows in $result - * @access public + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() */ function numRows($result) { $rows = @mysqli_num_rows($result); if ($rows === null) { - return $this->mysqlRaiseError(); + return $this->mysqliRaiseError(); } return $rows; } @@ -428,7 +554,12 @@ class DB_mysqli extends DB_common // {{{ autoCommit() /** - * Enable/disable automatic commits. + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { @@ -442,21 +573,23 @@ class DB_mysqli extends DB_common // {{{ commit() /** - * Commit the current transaction. + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { - return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } $result = @mysqli_query($this->connection, 'COMMIT'); $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); $this->transaction_opcount = 0; if (!$result) { - return $this->mysqlRaiseError(); + return $this->mysqliRaiseError(); } } return DB_OK; @@ -466,21 +599,23 @@ class DB_mysqli extends DB_common // {{{ rollback() /** - * Roll back (undo) the current transaction. + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { - return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } $result = @mysqli_query($this->connection, 'ROLLBACK'); $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); $this->transaction_opcount = 0; if (!$result) { - return $this->mysqlRaiseError(); + return $this->mysqliRaiseError(); } } return DB_OK; @@ -490,14 +625,15 @@ class DB_mysqli extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the data manipulation - * query. For other queries, this function returns 0. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return integer number of rows affected by the last query + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (DB::isManip($this->last_query)) { + if ($this->_last_query_manip) { return @mysqli_affected_rows($this->connection); } else { return 0; @@ -505,21 +641,6 @@ class DB_mysqli extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @return int native MySQL error code - * @access public - */ - function errorNative() - { - return @mysqli_errno($this->connection); - } - - // }}} // {{{ nextId() /** @@ -527,13 +648,13 @@ class DB_mysqli extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_mysqli::createSequence(), DB_mysqli::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -541,46 +662,51 @@ class DB_mysqli extends DB_common do { $repeat = 0; $this->pushErrorHandling(PEAR_ERROR_RETURN); - $result = $this->query("UPDATE ${seqname} ". - 'SET id=LAST_INSERT_ID(id+1)'); + $result = $this->query('UPDATE ' . $seqname + . ' SET id = LAST_INSERT_ID(id + 1)'); $this->popErrorHandling(); if ($result === DB_OK) { - /** COMMON CASE **/ + // COMMON CASE $id = @mysqli_insert_id($this->connection); if ($id != 0) { return $id; } - /** EMPTY SEQ TABLE **/ - // Sequence table must be empty for some reason, so fill it and return 1 + + // EMPTY SEQ TABLE + // Sequence table must be empty for some reason, + // so fill it and return 1 // Obtain a user-level lock - $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + $result = $this->getOne('SELECT GET_LOCK(' + . "'${seqname}_lock', 10)"); if (DB::isError($result)) { return $this->raiseError($result); } if ($result == 0) { - // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error - return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); } // add the default value - $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); + $result = $this->query('REPLACE INTO ' . $seqname + . ' (id) VALUES (0)'); if (DB::isError($result)) { return $this->raiseError($result); } // Release the lock - $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + $result = $this->getOne('SELECT RELEASE_LOCK(' + . "'${seqname}_lock')"); if (DB::isError($result)) { return $this->raiseError($result); } // We know what the result will be, so no need to try again return 1; - /** ONDEMAND TABLE CREATION **/ } elseif ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { + // ONDEMAND TABLE CREATION $result = $this->createSequence($seq_name); + // Since createSequence initializes the ID to be 1, // we do not need to retrieve the ID again (or we will get 2) if (DB::isError($result)) { @@ -590,10 +716,10 @@ class DB_mysqli extends DB_common return 1; } - /** BACKWARDS COMPAT **/ } elseif (DB::isError($result) && $result->getCode() == DB_ERROR_ALREADY_EXISTS) { + // BACKWARDS COMPAT // see _BCsequence() comment $result = $this->_BCsequence($seqname); if (DB::isError($result)) { @@ -611,19 +737,17 @@ class DB_mysqli extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_mysqli::nextID(), DB_mysqli::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); - $res = $this->query("CREATE TABLE ${seqname} ". - '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'. - ' PRIMARY KEY(id))'); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' + . ' PRIMARY KEY(id))'); if (DB::isError($res)) { return $res; } @@ -639,11 +763,10 @@ class DB_mysqli extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_mysql::nextID(), DB_mysql::createSequence() */ function dropSequence($seq_name) { @@ -655,10 +778,13 @@ class DB_mysqli extends DB_common /** * Backwards compatibility with old sequence emulation implementation - * (clean up the dupes). + * (clean up the dupes) + * + * @param string $seqname the sequence name to clean up + * + * @return bool true on success. A DB_Error object on failure. * - * @param string $seqname The sequence name to clean up - * @return mixed DB_Error or true + * @access private */ function _BCsequence($seqname) { @@ -672,17 +798,19 @@ class DB_mysqli extends DB_common if ($result == 0) { // Failed to get the lock, can't do the conversion, bail // with a DB_ERROR_NOT_LOCKED error - return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); } $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); if (DB::isError($highest_id)) { return $highest_id; } + // This should kill all rows except the highest // We should probably do something if $highest_id isn't // numeric, but I'm at a loss as how to handle that... - $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id"); + $result = $this->query('DELETE FROM ' . $seqname + . " WHERE id <> $highest_id"); if (DB::isError($result)) { return $result; } @@ -701,56 +829,64 @@ class DB_mysqli extends DB_common // {{{ quoteIdentifier() /** - * Quote a string so it can be safely used as a table or column name + * Quotes a string so it can be safely used as a table or column name + * (WARNING: using names that require this is a REALLY BAD IDEA) * - * Quoting style depends on which database driver is being used. - * - * MySQL can't handle the backtick character (`) in - * table or column names. + * WARNING: Older versions of MySQL can't handle the backtick + * character (`) in table or column names. * * @param string $str identifier name to be quoted * * @return string quoted identifier string * - * @since 1.6.0 - * @access public - * @internal + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { - return '`' . $str . '`'; + return '`' . str_replace('`', '``', $str) . '`'; } // }}} // {{{ escapeSimple() /** - * Escape a string according to the current DBMS's standards + * Escapes a string according to the current DBMS's standards * * @param string $str the string to be escaped * * @return string the escaped string * - * @internal + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 */ - function escapeSimple($str) { - return @mysqli_real_escape_string($this->connection, $str); - } - - // }}} - // {{{ modifyQuery() - - function modifyQuery($query) + function escapeSimple($str) { - return $query; + return @mysqli_real_escape_string($this->connection, $str); } // }}} // {{{ modifyLimitQuery() + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ function modifyLimitQuery($query, $from, $count, $params = array()) { - if (DB::isManip($query)) { + if (DB::isManip($query) || $this->_next_query_manip) { return $query . " LIMIT $count"; } else { return $query . " LIMIT $from, $count"; @@ -758,19 +894,21 @@ class DB_mysqli extends DB_common } // }}} - // {{{ mysqlRaiseError() + // {{{ mysqliRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see DB_common::errorCode() - * @see DB_common::raiseError() + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_mysqli::errorNative(), DB_common::errorCode() */ - function mysqlRaiseError($errno = null) + function mysqliRaiseError($errno = null) { if ($errno === null) { if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { @@ -791,32 +929,46 @@ class DB_mysqli extends DB_common } // }}} - // {{{ tableInfo() + // {{{ errorNative() /** - * Returns information about a table or a result set. + * Gets the DBMS' native error code produced by the last query * - * WARNING: this method will probably not work because the mysqli_*() - * functions it relies upon may not exist. + * @return int the DBMS' error code + */ + function errorNative() + { + return @mysqli_errno($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal - * @see DB_common::tableInfo() + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::setOption() */ - function tableInfo($result, $mode = null) { - if (isset($result->result)) { - /* - * Probably received a result object. - * Extract the result resource identifier. - */ - $id = $result->result; - $got_string = false; - } elseif (is_string($result)) { + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + // Fix for bug #11580. + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); + } + } + /* * Probably received a table name. * Create a result resource identifier. @@ -824,6 +976,13 @@ class DB_mysqli extends DB_common $id = @mysqli_query($this->connection, "SELECT * FROM $result LIMIT 0"); $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; } else { /* * Probably received a result resource identifier. @@ -835,7 +994,7 @@ class DB_mysqli extends DB_common } if (!is_a($id, 'mysqli_result')) { - return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); + return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { @@ -845,58 +1004,42 @@ class DB_mysqli extends DB_common } $count = @mysqli_num_fields($id); + $res = array(); - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - for ($i=0; $i<$count; $i++) { - $tmp = @mysqli_fetch_field($id); - $res[$i]['table'] = $case_func($tmp->table); - $res[$i]['name'] = $case_func($tmp->name); - $res[$i]['type'] = isset($this->mysqli_types[$tmp->type]) ? - $this->mysqli_types[$tmp->type] : - 'unknown'; - $res[$i]['len'] = $tmp->max_length; - - $res[$i]['flags'] = ''; - foreach ($this->mysqli_flags as $const => $means) { - if ($tmp->flags & $const) { - $res[$i]['flags'] .= $means . ' '; - } - } - if ($tmp->def) { - $res[$i]['flags'] .= 'default_' . rawurlencode($tmp->def); + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $tmp = @mysqli_fetch_field($id); + + $flags = ''; + foreach ($this->mysqli_flags as $const => $means) { + if ($tmp->flags & $const) { + $flags .= $means . ' '; } - $res[$i]['flags'] = trim($res[$i]['flags']); } - } else { // full - $res['num_fields']= $count; - - for ($i=0; $i<$count; $i++) { - $tmp = @mysqli_fetch_field($id); - $res[$i]['table'] = $case_func($tmp->table); - $res[$i]['name'] = $case_func($tmp->name); - $res[$i]['type'] = isset($this->mysqli_types[$tmp->type]) ? - $this->mysqli_types[$tmp->type] : - 'unknown'; - $res[$i]['len'] = $tmp->max_length; - - $res[$i]['flags'] = ''; - foreach ($this->mysqli_flags as $const => $means) { - if ($tmp->flags & $const) { - $res[$i]['flags'] .= $means . ' '; - } - } - if ($tmp->def) { - $res[$i]['flags'] .= 'default_' . rawurlencode($tmp->def); - } - $res[$i]['flags'] = trim($res[$i]['flags']); + if ($tmp->def) { + $flags .= 'default_' . rawurlencode($tmp->def); + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($tmp->table), + 'name' => $case_func($tmp->name), + 'type' => isset($this->mysqli_types[$tmp->type]) + ? $this->mysqli_types[$tmp->type] + : 'unknown', + // http://bugs.php.net/?id=36579 + 'len' => $tmp->length, + 'flags' => $flags, + ); - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -911,34 +1054,23 @@ class DB_mysqli extends DB_common // {{{ getSpecialQuery() /** - * Returns the query needed to get some backend info. + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested * - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string + * @access protected + * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SHOW TABLES'; - case 'views': - return DB_ERROR_NOT_CAPABLE; case 'users': - $sql = 'select distinct User from user'; - if ($this->dsn['database'] != 'mysql') { - $dsn = $this->dsn; - $dsn['database'] = 'mysql'; - if (DB::isError($db = DB::connect($dsn))) { - return $db; - } - $sql = $db->getCol($sql); - $db->disconnect(); - // XXX Fixme the mysql driver should take care of this - if (!@mysqli_select_db($this->connection, $this->dsn['database'])) { - return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); - } - } - return $sql; + return 'SELECT DISTINCT User FROM mysql.user'; case 'databases': return 'SHOW DATABASES'; default: @@ -946,7 +1078,7 @@ class DB_mysqli extends DB_common } } - // }}} + // }}} } diff --git a/thirdparty/pear/DB/oci8.php b/thirdparty/pear/DB/oci8.php old mode 100644 new mode 100755 index 42f48b0..3dfee11 --- a/thirdparty/pear/DB/oci8.php +++ b/thirdparty/pear/DB/oci8.php @@ -1,56 +1,142 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// be aware... OCIError() only appears to return anything when given a -// statement, so functions return the generic DB_ERROR instead of more -// useful errors that have to do with feedback from the database. +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's oci8 extension + * for interacting with Oracle databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author James L. Pine + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: oci8.php,v 1.115 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's Oracle 8 - * call-interface extension. + * The methods PEAR DB uses to interact with PHP's oci8 extension + * for interacting with Oracle databases * * Definitely works with versions 8 and 9 of Oracle. * - * @package DB - * @version $Id$ - * @category Database - * @author James L. Pine + * These methods overload the ones declared in DB_common. + * + * Be aware... OCIError() only appears to return anything when given a + * statement, so functions return the generic DB_ERROR instead of more + * useful errors that have to do with feedback from the database. + * + * @category Database + * @package DB + * @author James L. Pine + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_oci8 extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'oci8'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'oci8'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '5.0.0', + 'numrows' => 'subquery', + 'pconnect' => true, + 'prepare' => true, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1 => DB_ERROR_CONSTRAINT, + 900 => DB_ERROR_SYNTAX, + 904 => DB_ERROR_NOSUCHFIELD, + 913 => DB_ERROR_VALUE_COUNT_ON_ROW, + 921 => DB_ERROR_SYNTAX, + 923 => DB_ERROR_SYNTAX, + 942 => DB_ERROR_NOSUCHTABLE, + 955 => DB_ERROR_ALREADY_EXISTS, + 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1401 => DB_ERROR_INVALID, + 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1418 => DB_ERROR_NOT_FOUND, + 1476 => DB_ERROR_DIVZERO, + 1722 => DB_ERROR_INVALID_NUMBER, + 2289 => DB_ERROR_NOSUCHTABLE, + 2291 => DB_ERROR_CONSTRAINT, + 2292 => DB_ERROR_CONSTRAINT, + 2449 => DB_ERROR_CONSTRAINT, + 12899 => DB_ERROR_INVALID, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $manip_query = array(); - var $prepare_types = array(); - var $autoCommit = 1; - var $last_stmt = false; /** - * stores the $data passed to execute() in the oci8 driver + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * Stores the $data passed to execute() in the oci8 driver * * Gets reset to array() when simpleQuery() is run. * @@ -62,77 +148,134 @@ class DB_oci8 extends DB_common */ var $_data = array(); + /** + * The result or statement handle from the most recently executed query + * @var resource + */ + var $last_stmt; + + /** + * Is the given prepared statement a data manipulation query? + * @var array + * @access private + */ + var $manip_query = array(); + + /** + * Store of prepared SQL queries. + * @var array + * @access private + */ + var $_prepared_queries = array(); + + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_oci8() { $this->DB_common(); - $this->phptype = 'oci8'; - $this->dbsyntax = 'oci8'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => true, - 'limit' => 'alter' - ); - $this->errorcode_map = array( - 1 => DB_ERROR_CONSTRAINT, - 900 => DB_ERROR_SYNTAX, - 904 => DB_ERROR_NOSUCHFIELD, - 921 => DB_ERROR_SYNTAX, - 923 => DB_ERROR_SYNTAX, - 942 => DB_ERROR_NOSUCHTABLE, - 955 => DB_ERROR_ALREADY_EXISTS, - 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, - 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, - 1476 => DB_ERROR_DIVZERO, - 1722 => DB_ERROR_INVALID_NUMBER, - 2289 => DB_ERROR_NOSUCHTABLE, - 2291 => DB_ERROR_CONSTRAINT, - 2292 => DB_ERROR_CONSTRAINT, - 2449 => DB_ERROR_CONSTRAINT, - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent + * If PHP is at version 5.0.0 or greater: + * + Generally, oci_connect() or oci_pconnect() are used. + * + But if the new_link DSN option is set to true, oci_new_connect() + * is used. * - * @return int DB_OK on success, a DB error code on failure + * When using PHP version 4.x, OCILogon() or OCIPLogon() are used. + * + * PEAR DB's oci8 driver supports the following extra DSN options: + * + charset The character set to be used on the connection. + * Only used if PHP is at version 5.0.0 or greater + * and the Oracle server is at 9.2 or greater. + * Available since PEAR DB 1.7.0. + * + new_link If set to true, causes subsequent calls to + * connect() to return a new connection link + * instead of the existing one. WARNING: this is + * not portable to other DBMS's. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('oci8')) { + if (!PEAR::loadExtension('oci8')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + // Backwards compatibility with DB < 1.7.0 + if (empty($dsn['database']) && !empty($dsn['hostspec'])) { + $db = $dsn['hostspec']; + } else { + $db = $dsn['database']; + } + + if (function_exists('oci_connect')) { + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + $connect_function = 'oci_new_connect'; + } else { + $connect_function = $persistent ? 'oci_pconnect' + : 'oci_connect'; + } + if (isset($this->dsn['port']) && $this->dsn['port']) { + $db = '//'.$db.':'.$this->dsn['port']; + } - if ($dsninfo['hostspec']) { - $conn = @$connect_function($dsninfo['username'], - $dsninfo['password'], - $dsninfo['hostspec']); - } elseif ($dsninfo['username'] || $dsninfo['password']) { - $conn = @$connect_function($dsninfo['username'], - $dsninfo['password']); + $char = empty($dsn['charset']) ? null : $dsn['charset']; + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + $db, + $char); + $error = OCIError(); + if (!empty($error) && $error['code'] == 12541) { + // Couldn't find TNS listener. Try direct connection. + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + null, + $char); + } } else { - $conn = false; + $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; + if ($db) { + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + $db); + } elseif ($dsn['username'] || $dsn['password']) { + $this->connection = @$connect_function($dsn['username'], + $dsn['password']); + } } - if ($conn == false) { + + if (!$this->connection) { $error = OCIError(); $error = (is_array($error)) ? $error['message'] : null; - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $error); + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $error); } - $this->connection = $conn; return DB_OK; } @@ -140,13 +283,17 @@ class DB_oci8 extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. + * Disconnects from the database server * - * @return bool true on success, false if not connected. + * @return bool TRUE on success, FALSE on failure */ function disconnect() { - $ret = @OCILogOff($this->connection); + if (function_exists('oci_close')) { + $ret = @oci_close($this->connection); + } else { + $ret = @OCILogOff($this->connection); + } $this->connection = null; return $ret; } @@ -155,25 +302,29 @@ class DB_oci8 extends DB_common // {{{ simpleQuery() /** - * Send a query to oracle and return the results as an oci8 resource - * identifier. + * Sends a query to the database server + * + * To determine how many rows of a result set get buffered using + * ocisetprefetch(), see the "result_buffering" option in setOptions(). + * This option was added in Release 1.7.0. * - * @param $query the SQL query + * @param string the SQL query string * - * @return int returns a valid oci8 result for successful SELECT - * queries, DB_OK for other successful queries. A DB error code - * is returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { $this->_data = array(); + $this->last_parameters = array(); $this->last_query = $query; $query = $this->modifyQuery($query); $result = @OCIParse($this->connection, $query); if (!$result) { return $this->oci8RaiseError(); } - if ($this->autoCommit) { + if ($this->autocommit) { $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); } else { $success = @OCIExecute($result,OCI_DEFAULT); @@ -181,10 +332,13 @@ class DB_oci8 extends DB_common if (!$success) { return $this->oci8RaiseError($result); } - $this->last_stmt=$result; - // Determine which queries that should return data, and which - // should return an error code only. - return DB::isManip($query) ? DB_OK : $result; + $this->last_stmt = $result; + if ($this->_checkManip($query)) { + return DB_OK; + } else { + @ocisetprefetch($result, $this->options['result_buffering']); + return $result; + } } // }}} @@ -208,24 +362,26 @@ class DB_oci8 extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); @@ -256,26 +412,43 @@ class DB_oci8 extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set * - * @param $result oci8 result identifier + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @return bool true on success, false if $result is invalid + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() */ function freeResult($result) { - return @OCIFreeStatement($result); + return is_resource($result) ? OCIFreeStatement($result) : false; } /** - * Free the internal resources associated with a prepared query. + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. * - * @param $stmt oci8 statement identifier + * @return bool TRUE on success, FALSE if $result is invalid * - * @return bool true on success, false if $result is invalid + * @see DB_oci8::prepare() */ - function freePrepared($stmt) + function freePrepared($stmt, $free_resource = true) { + if (!is_resource($stmt)) { + return false; + } + if ($free_resource) { + @ocifreestatement($stmt); + } if (isset($this->prepare_types[(int)$stmt])) { unset($this->prepare_types[(int)$stmt]); unset($this->manip_query[(int)$stmt]); @@ -288,6 +461,22 @@ class DB_oci8 extends DB_common // }}} // {{{ numRows() + /** + * Gets the number of rows in a result set + * + * Only works if the DB_PORTABILITY_NUMROWS portability option + * is turned on. + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows(), DB_common::setOption() + */ function numRows($result) { // emulate numRows for Oracle. yuck. @@ -298,20 +487,18 @@ class DB_oci8 extends DB_common $save_query = $this->last_query; $save_stmt = $this->last_stmt; - if (count($this->_data)) { - $smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')'); - $count = $this->execute($smt, $this->_data); - } else { - $count =& $this->query($countquery); - } + $count = $this->query($countquery); + // Restore the last query and statement. + $this->last_query = $save_query; + $this->last_stmt = $save_stmt; + if (DB::isError($count) || DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) { - $this->last_query = $save_query; - $this->last_stmt = $save_stmt; return $this->raiseError(DB_ERROR_NOT_CAPABLE); } + return $row[0]; } return $this->raiseError(DB_ERROR_NOT_CAPABLE); @@ -321,11 +508,17 @@ class DB_oci8 extends DB_common // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource * - * @param $result oci8 result identifier + * @return int the number of columns. A DB_Error object on failure. * - * @return int the number of columns per row in $result + * @see DB_result::numCols() */ function numCols($result) { @@ -337,30 +530,6 @@ class DB_oci8 extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error code of the last error (if any) that occured - * on the current connection. This does not work, as OCIError does - * not work unless given a statement. If OCIError does return - * something, so will this. - * - * @return int native oci8 error code - */ - function errorNative() - { - if (is_resource($this->last_stmt)) { - $error = @OCIError($this->last_stmt); - } else { - $error = @OCIError($this->connection); - } - if (is_array($error)) { - return $error['code']; - } - return false; - } - - // }}} // {{{ prepare() /** @@ -384,8 +553,11 @@ class DB_oci8 extends DB_common * "UPDATE foo SET col=? WHERE col='over \& under'" * * - * @param string $query query to be prepared - * @return mixed DB statement resource on success. DB_Error on failure. + * @param string $query the query to be prepared + * + * @return mixed DB statement resource on success. DB_Error on failure. + * + * @see DB_oci8::execute() */ function prepare($query) { @@ -427,6 +599,7 @@ class DB_oci8 extends DB_common } $this->prepare_types[(int)$stmt] = $types; $this->manip_query[(int)$stmt] = DB::isManip($query); + $this->_prepared_queries[(int)$stmt] = $newquery; return $stmt; } @@ -436,28 +609,33 @@ class DB_oci8 extends DB_common /** * Executes a DB statement prepared with prepare(). * + * To determine how many rows of a result set get buffered using + * ocisetprefetch(), see the "result_buffering" option in setOptions(). + * This option was added in Release 1.7.0. + * * @param resource $stmt a DB statement resource returned from prepare() * @param mixed $data array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 for non-array items or the * quantity of elements in the array. - * @return int returns an oci8 result resource for successful - * SELECT queries, DB_OK for other successful queries. A DB error - * code is returned on failure. - * @see DB_oci::prepare() + * + * @return mixed returns an oic8 result resource for successful SELECT + * queries, DB_OK for other successful queries. + * A DB error object is returned on failure. + * + * @see DB_oci8::prepare() */ function &execute($stmt, $data = array()) { - if (!is_array($data)) { - $data = array($data); - } - + $data = (array)$data; + $this->last_parameters = $data; + $this->last_query = $this->_prepared_queries[(int)$stmt]; $this->_data = $data; - $types =& $this->prepare_types[(int)$stmt]; + $types = $this->prepare_types[(int)$stmt]; if (count($types) != count($data)) { - $tmp =& $this->raiseError(DB_ERROR_MISMATCH); + $tmp = $this->raiseError(DB_ERROR_MISMATCH); return $tmp; } @@ -476,19 +654,26 @@ class DB_oci8 extends DB_common } elseif ($types[$i] == DB_PARAM_OPAQUE) { $fp = @fopen($data[$key], 'rb'); if (!$fp) { - $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); return $tmp; } $data[$key] = fread($fp, filesize($data[$key])); fclose($fp); + } elseif ($types[$i] == DB_PARAM_SCALAR) { + // Floats have to be converted to a locale-neutral + // representation. + if (is_float($data[$key])) { + $data[$key] = $this->quoteFloat($data[$key]); + } } if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { $tmp = $this->oci8RaiseError($stmt); return $tmp; } + $this->last_query = str_replace(':bind'.$i, $this->quoteSmart($data[$key]), $this->last_query); $i++; } - if ($this->autoCommit) { + if ($this->autocommit) { $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); } else { $success = @OCIExecute($stmt, OCI_DEFAULT); @@ -498,10 +683,14 @@ class DB_oci8 extends DB_common return $tmp; } $this->last_stmt = $stmt; - if ($this->manip_query[(int)$stmt]) { + if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { + $this->_last_query_manip = true; + $this->_next_query_manip = false; $tmp = DB_OK; } else { - $tmp =& new DB_result($this, $stmt); + $this->_last_query_manip = false; + @ocisetprefetch($stmt, $this->options['result_buffering']); + $tmp = new DB_result($this, $stmt); } return $tmp; } @@ -510,13 +699,16 @@ class DB_oci8 extends DB_common // {{{ autoCommit() /** - * Enable/disable automatic commits + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off * - * @param $onoff true/false whether to autocommit + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { - $this->autoCommit = (bool)$onoff;; + $this->autocommit = (bool)$onoff;; return DB_OK; } @@ -524,9 +716,9 @@ class DB_oci8 extends DB_common // {{{ commit() /** - * Commit transactions on the current connection + * Commits the current transaction * - * @return DB_ERROR or DB_OK + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @@ -541,9 +733,9 @@ class DB_oci8 extends DB_common // {{{ rollback() /** - * Roll back all uncommitted transactions on the current connection. + * Reverts the current transaction * - * @return DB_ERROR or DB_OK + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @@ -558,10 +750,11 @@ class DB_oci8 extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the last query. - * if the last query was a select, returns 0. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return number of rows affected by the last query or DB_ERROR + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { @@ -578,9 +771,19 @@ class DB_oci8 extends DB_common // }}} // {{{ modifyQuery() + /** + * Changes a query string for various DBMS specific reasons + * + * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + */ function modifyQuery($query) { - // "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle if (preg_match('/^\s*SELECT/i', $query) && !preg_match('/\sFROM\s/i', $query)) { $query .= ' FROM dual'; @@ -592,14 +795,20 @@ class DB_oci8 extends DB_common // {{{ modifyLimitQuery() /** - * Emulate the row limit support altering the query + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. * - * @param string $query The query to treat - * @param int $from The row to start to fetch from - * @param int $count The offset - * @return string The modified query + * @return string the query string with LIMIT clauses added * - * @author Tomas V.V.Cox + * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { @@ -609,7 +818,7 @@ class DB_oci8 extends DB_common if (count($params)) { $result = $this->prepare("SELECT * FROM ($query) " . 'WHERE NULL = NULL'); - $tmp =& $this->execute($result, $params); + $tmp = $this->execute($result, $params); } else { $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; @@ -655,13 +864,13 @@ class DB_oci8 extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_oci8::createSequence(), DB_oci8::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -669,7 +878,7 @@ class DB_oci8 extends DB_common $repeat = 0; do { $this->expectError(DB_ERROR_NOSUCHTABLE); - $result =& $this->query("SELECT ${seqname}.nextval FROM dual"); + $result = $this->query("SELECT ${seqname}.nextval FROM dual"); $this->popExpect(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { @@ -694,17 +903,15 @@ class DB_oci8 extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_oci8::nextID(), DB_oci8::dropSequence() */ function createSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("CREATE SEQUENCE ${seqname}"); + return $this->query('CREATE SEQUENCE ' + . $this->getSequenceName($seq_name)); } // }}} @@ -715,30 +922,31 @@ class DB_oci8 extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_oci8::nextID(), DB_oci8::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP SEQUENCE ${seqname}"); + return $this->query('DROP SEQUENCE ' + . $this->getSequenceName($seq_name)); } // }}} // {{{ oci8RaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see DB_common::errorCode() - * @see DB_common::raiseError() + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_oci8::errorNative(), DB_oci8::errorCode() */ function oci8RaiseError($errno = null) { @@ -755,28 +963,32 @@ class DB_oci8 extends DB_common } // }}} - // {{{ getSpecialQuery() + // {{{ errorNative() /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code. FALSE if the code could not be + * determined */ - function getSpecialQuery($type) + function errorNative() { - switch ($type) { - case 'tables': - return 'SELECT table_name FROM user_tables'; - default: - return null; + if (is_resource($this->last_stmt)) { + $error = @OCIError($this->last_stmt); + } else { + $error = @OCIError($this->connection); } + if (is_array($error)) { + return $error['code']; + } + return false; } // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. @@ -784,12 +996,15 @@ class DB_oci8 extends DB_common * NOTE: flags won't contain index information. * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) @@ -800,6 +1015,8 @@ class DB_oci8 extends DB_common $case_func = 'strval'; } + $res = array(); + if (is_string($result)) { /* * Probably received a table name. @@ -819,15 +1036,16 @@ class DB_oci8 extends DB_common if (!@OCIExecute($stmt, OCI_DEFAULT)) { return $this->oci8RaiseError($stmt); } - + $i = 0; while (@OCIFetch($stmt)) { - $res[$i]['table'] = $case_func($result); - $res[$i]['name'] = $case_func(@OCIResult($stmt, 1)); - $res[$i]['type'] = @OCIResult($stmt, 2); - $res[$i]['len'] = @OCIResult($stmt, 3); - $res[$i]['flags'] = (@OCIResult($stmt, 4) == 'N') ? 'not_null' : ''; - + $res[$i] = array( + 'table' => $case_func($result), + 'name' => $case_func(@OCIResult($stmt, 1)), + 'type' => @OCIResult($stmt, 2), + 'len' => @OCIResult($stmt, 3), + 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', + ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } @@ -849,23 +1067,23 @@ class DB_oci8 extends DB_common * Extract the result resource identifier. */ $result = $result->result; - } else { - /* - * ELSE, probably received a result resource identifier. - * Deprecated. Here for compatibility only. - */ } + $res = array(); + if ($result === $this->last_stmt) { $count = @OCINumCols($result); - - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = ''; - $res[$i]['name'] = $case_func(@OCIColumnName($result, $i+1)); - $res[$i]['type'] = @OCIColumnType($result, $i+1); - $res[$i]['len'] = @OCIColumnSize($result, $i+1); - $res[$i]['flags'] = ''; - + if ($mode) { + $res['num_fields'] = $count; + } + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => '', + 'name' => $case_func(@OCIColumnName($result, $i+1)), + 'type' => @OCIColumnType($result, $i+1), + 'len' => @OCIColumnSize($result, $i+1), + 'flags' => '', + ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } @@ -873,11 +1091,6 @@ class DB_oci8 extends DB_common $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } - - if ($mode) { - $res['num_fields'] = $count; - } - } else { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } @@ -886,6 +1099,50 @@ class DB_oci8 extends DB_common } // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT table_name FROM user_tables'; + case 'synonyms': + return 'SELECT synonym_name FROM user_synonyms'; + case 'views': + return 'SELECT view_name FROM user_views'; + default: + return null; + } + } + + // }}} + // {{{ quoteFloat() + + /** + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. + */ + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); + } + + // }}} } diff --git a/thirdparty/pear/DB/odbc.php b/thirdparty/pear/DB/odbc.php old mode 100644 new mode 100755 index d0f50a1..fecc548 --- a/thirdparty/pear/DB/odbc.php +++ b/thirdparty/pear/DB/odbc.php @@ -1,172 +1,242 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// XXX legend: -// More info on ODBC errors could be found here: -// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp -// -// XXX ERRORMSG: The error message from the odbc function should -// be registered here. +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's odbc extension + * for interacting with databases via ODBC connections + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: odbc.php,v 1.81 2007/07/06 05:19:21 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's ODBC - * extension. + * The methods PEAR DB uses to interact with PHP's odbc extension + * for interacting with databases via ODBC connections + * + * These methods overload the ones declared in DB_common. * - * @package DB - * @version $Id$ - * @category Database - * @author Stig Bakken + * More info on ODBC errors could be found here: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_odbc extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'odbc'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sql92'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * NOTE: The feature set of the following drivers are different than + * the default: + * + solid: 'transactions' = true + * + navision: 'limit' = false + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + '01004' => DB_ERROR_TRUNCATED, + '07001' => DB_ERROR_MISMATCH, + '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW, + '21S02' => DB_ERROR_MISMATCH, + '22001' => DB_ERROR_INVALID, + '22003' => DB_ERROR_INVALID_NUMBER, + '22005' => DB_ERROR_INVALID_NUMBER, + '22008' => DB_ERROR_INVALID_DATE, + '22012' => DB_ERROR_DIVZERO, + '23000' => DB_ERROR_CONSTRAINT, + '23502' => DB_ERROR_CONSTRAINT_NOT_NULL, + '23503' => DB_ERROR_CONSTRAINT, + '23504' => DB_ERROR_CONSTRAINT, + '23505' => DB_ERROR_CONSTRAINT, + '24000' => DB_ERROR_INVALID, + '34000' => DB_ERROR_INVALID, + '37000' => DB_ERROR_SYNTAX, + '42000' => DB_ERROR_SYNTAX, + '42601' => DB_ERROR_SYNTAX, + 'IM001' => DB_ERROR_UNSUPPORTED, + 'S0000' => DB_ERROR_NOSUCHTABLE, + 'S0001' => DB_ERROR_ALREADY_EXISTS, + 'S0002' => DB_ERROR_NOSUCHTABLE, + 'S0011' => DB_ERROR_ALREADY_EXISTS, + 'S0012' => DB_ERROR_NOT_FOUND, + 'S0021' => DB_ERROR_ALREADY_EXISTS, + 'S0022' => DB_ERROR_NOSUCHFIELD, + 'S1009' => DB_ERROR_INVALID, + 'S1090' => DB_ERROR_INVALID, + 'S1C00' => DB_ERROR_NOT_CAPABLE, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $row = array(); + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_odbc() { $this->DB_common(); - $this->phptype = 'odbc'; - $this->dbsyntax = 'sql92'; - $this->features = array( - 'prepare' => true, - 'pconnect' => true, - 'transactions' => false, - 'limit' => 'emulate' - ); - $this->errorcode_map = array( - '01004' => DB_ERROR_TRUNCATED, - '07001' => DB_ERROR_MISMATCH, - '21S01' => DB_ERROR_MISMATCH, - '21S02' => DB_ERROR_MISMATCH, - '22003' => DB_ERROR_INVALID_NUMBER, - '22005' => DB_ERROR_INVALID_NUMBER, - '22008' => DB_ERROR_INVALID_DATE, - '22012' => DB_ERROR_DIVZERO, - '23000' => DB_ERROR_CONSTRAINT, - '23502' => DB_ERROR_CONSTRAINT_NOT_NULL, - '23503' => DB_ERROR_CONSTRAINT, - '23505' => DB_ERROR_CONSTRAINT, - '24000' => DB_ERROR_INVALID, - '34000' => DB_ERROR_INVALID, - '37000' => DB_ERROR_SYNTAX, - '42000' => DB_ERROR_SYNTAX, - '42601' => DB_ERROR_SYNTAX, - 'IM001' => DB_ERROR_UNSUPPORTED, - 'S0000' => DB_ERROR_NOSUCHTABLE, - 'S0001' => DB_ERROR_ALREADY_EXISTS, - 'S0002' => DB_ERROR_NOSUCHTABLE, - 'S0011' => DB_ERROR_ALREADY_EXISTS, - 'S0012' => DB_ERROR_NOT_FOUND, - 'S0021' => DB_ERROR_ALREADY_EXISTS, - 'S0022' => DB_ERROR_NOSUCHFIELD, - 'S1000' => DB_ERROR_CONSTRAINT_NOT_NULL, - 'S1009' => DB_ERROR_INVALID, - 'S1090' => DB_ERROR_INVALID, - 'S1C00' => DB_ERROR_NOT_CAPABLE - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent + * PEAR DB's odbc driver supports the following extra DSN options: + * + cursor The type of cursor to be used for this connection. * - * @return int DB_OK on success, a DB error code on failure + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('odbc')) { + if (!PEAR::loadExtension('odbc')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - if ($dsninfo['dbsyntax']) { - $this->dbsyntax = $dsninfo['dbsyntax']; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; } switch ($this->dbsyntax) { + case 'access': + case 'db2': case 'solid': - $this->features = array( - 'prepare' => true, - 'pconnect' => true, - 'transactions' => true - ); + $this->features['transactions'] = true; break; case 'navision': - // the Navision driver doesn't support fetch row by number $this->features['limit'] = false; } /* - * This is hear for backwards compatibility. - * Should have been using 'database' all along, but used hostspec. + * This is hear for backwards compatibility. Should have been using + * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used. */ - if ($dsninfo['database']) { - $odbcdsn = $dsninfo['database']; - } elseif ($dsninfo['hostspec']) { - $odbcdsn = $dsninfo['hostspec']; + if ($dsn['database']) { + $odbcdsn = $dsn['database']; + } elseif ($dsn['hostspec']) { + $odbcdsn = $dsn['hostspec']; } else { $odbcdsn = 'localhost'; } - if ($this->provides('pconnect')) { - $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; - } else { - $connect_function = 'odbc_connect'; - } + $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; - if (empty($dsninfo['cursor'])) { - $conn = @$connect_function($odbcdsn, $dsninfo['username'], - $dsninfo['password']); + if (empty($dsn['cursor'])) { + $this->connection = @$connect_function($odbcdsn, $dsn['username'], + $dsn['password']); } else { - $conn = @$connect_function($odbcdsn, $dsninfo['username'], - $dsninfo['password'], - $dsninfo['cursor']); + $this->connection = @$connect_function($odbcdsn, $dsn['username'], + $dsn['password'], + $dsn['cursor']); } - if (!is_resource($conn)) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, - null, $this->errorNative()); + if (!is_resource($this->connection)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $this->errorNative()); } - $this->connection = $conn; return DB_OK; } // }}} // {{{ disconnect() + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ function disconnect() { $err = @odbc_close($this->connection); @@ -178,14 +248,13 @@ class DB_odbc extends DB_common // {{{ simpleQuery() /** - * Send a query to ODBC and return the results as a ODBC resource - * identifier. + * Sends a query to the database server * - * @param $query the SQL query + * @param string the SQL query string * - * @return int returns a valid ODBC result for successful SELECT - * queries, DB_OK for other successful queries. A DB error code - * is returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { @@ -197,12 +266,11 @@ class DB_odbc extends DB_common } // Determine which queries that should return data, and which // should return an error code only. - if (DB::isManip($query)) { - $this->manip_result = $result; // For affectedRows() + if ($this->_checkManip($query)) { + $this->affected = $result; // For affectedRows() return DB_OK; } - $this->row[(int)$result] = 0; - $this->manip_result = 0; + $this->affected = 0; return $result; } @@ -227,24 +295,26 @@ class DB_odbc extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { $arr = array(); if ($rownum !== null) { @@ -257,13 +327,7 @@ class DB_odbc extends DB_common } else { $cols = @odbc_fetch_into($result, $arr); } - if (!$cols) { - /* XXX FIXME: doesn't work with unixODBC and easysoft - (get corrupted $errno values) - if ($errno = @odbc_error($this->connection)) { - return $this->RaiseError($errno); - }*/ return null; } if ($fetchmode !== DB_FETCHMODE_ORDERED) { @@ -288,15 +352,40 @@ class DB_odbc extends DB_common // }}} // {{{ freeResult() + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ function freeResult($result) { - unset($this->row[(int)$result]); - return @odbc_free_result($result); + return is_resource($result) ? odbc_free_result($result) : false; } // }}} // {{{ numCols() + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ function numCols($result) { $cols = @odbc_num_fields($result); @@ -310,17 +399,18 @@ class DB_odbc extends DB_common // {{{ affectedRows() /** - * Returns the number of rows affected by a manipulative query - * (INSERT, DELETE, UPDATE) - * @return mixed int affected rows, 0 when non manip queries or - * DB error on error + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (empty($this->manip_result)) { // In case of SELECT stms + if (empty($this->affected)) { // In case of SELECT stms return 0; } - $nrows = @odbc_num_rows($this->manip_result); + $nrows = @odbc_num_rows($this->affected); if ($nrows == -1) { return $this->odbcRaiseError(); } @@ -331,11 +421,20 @@ class DB_odbc extends DB_common // {{{ numRows() /** - * ODBC may or may not support counting rows in the result set of - * SELECTs. + * Gets the number of rows in a result set + * + * Not all ODBC drivers support this functionality. If they don't + * a DB_Error object for DB_ERROR_UNSUPPORTED is returned. + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. * - * @param $result the odbc result resource - * @return the number of rows, or 0 + * @see DB_result::numRows() */ function numRows($result) { @@ -343,6 +442,9 @@ class DB_odbc extends DB_common if ($nrows == -1) { return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED); } + if ($nrows === false) { + return $this->odbcRaiseError(); + } return $nrows; } @@ -350,9 +452,7 @@ class DB_odbc extends DB_common // {{{ quoteIdentifier() /** - * Quote a string so it can be safely used as a table / column name - * - * Quoting style depends on which dbsyntax was passed in the DSN. + * Quotes a string so it can be safely used as a table or column name * * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked * "Use ANSI quoted identifiers" when setting up the ODBC data source. @@ -361,8 +461,8 @@ class DB_odbc extends DB_common * * @return string quoted identifier string * - * @since 1.6.0 - * @access public + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { @@ -387,27 +487,9 @@ class DB_odbc extends DB_common * @deprecated Deprecated in release 1.6.0 * @internal */ - function quote($str) { - return $this->quoteSmart($str); - } - - // }}} - // {{{ errorNative() - - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @access public - * - * @return int ODBC error code - */ - function errorNative() + function quote($str) { - if (!isset($this->connection) || !is_resource($this->connection)) { - return @odbc_error() . ' ' . @odbc_errormsg(); - } - return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); + return $this->quoteSmart($str); } // }}} @@ -418,13 +500,13 @@ class DB_odbc extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_odbc::createSequence(), DB_odbc::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -471,19 +553,17 @@ class DB_odbc extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_odbc::nextID(), DB_odbc::dropSequence() */ function createSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("CREATE TABLE ${seqname} ". - '(id integer NOT NULL,'. - ' PRIMARY KEY(id))'); + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' (id integer NOT NULL,' + . ' PRIMARY KEY(id))'); } // }}} @@ -494,21 +574,27 @@ class DB_odbc extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_odbc::nextID(), DB_odbc::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP TABLE ${seqname}"); + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ autoCommit() + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ function autoCommit($onoff = false) { if (!@odbc_autocommit($this->connection, $onoff)) { @@ -520,6 +606,11 @@ class DB_odbc extends DB_common // }}} // {{{ commit() + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ function commit() { if (!@odbc_commit($this->connection)) { @@ -531,6 +622,11 @@ class DB_odbc extends DB_common // }}} // {{{ rollback() + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ function rollback() { if (!@odbc_rollback($this->connection)) { @@ -543,15 +639,16 @@ class DB_odbc extends DB_common // {{{ odbcRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. - * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see errorNative() - * @see DB_common::errorCode() - * @see DB_common::raiseError() + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_odbc::errorNative(), DB_common::errorCode() */ function odbcRaiseError($errno = null) { @@ -564,11 +661,212 @@ class DB_odbc extends DB_common // Doing this in case mode changes during runtime. $this->errorcode_map['07001'] = DB_ERROR_MISMATCH; } + + $native_code = odbc_error($this->connection); + + // S1000 is for "General Error." Let's be more specific. + if ($native_code == 'S1000') { + $errormsg = odbc_errormsg($this->connection); + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/includes related records.$/i' => DB_ERROR_CONSTRAINT, + '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL, + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $this->raiseError($code, + null, null, null, + $native_code . ' ' . $errormsg); + } + } + $errno = DB_ERROR; + } else { + $errno = $this->errorCode($native_code); + } + break; + default: + $errno = $this->errorCode(odbc_error($this->connection)); } - $errno = $this->errorCode(odbc_error($this->connection)); } return $this->raiseError($errno, null, null, null, - $this->errorNative()); + $this->errorNative()); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code and message produced by the last query + * + * @return string the DBMS' error code and message + */ + function errorNative() + { + if (!is_resource($this->connection)) { + return @odbc_error() . ' ' . @odbc_errormsg(); + } + return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @odbc_exec($this->connection, "SELECT * FROM $result"); + if (!$id) { + return $this->odbcRaiseError(); + } + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @odbc_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $col = $i + 1; + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func(@odbc_field_name($id, $col)), + 'type' => @odbc_field_type($id, $col), + 'len' => @odbc_field_len($id, $col), + 'flags' => '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @odbc_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com. + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the list of objects requested + * + * @access protected + * @see DB_common::getListOf() + * @since Method available since Release 1.7.0 + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'databases': + if (!function_exists('odbc_data_source')) { + return null; + } + $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST); + if (is_array($res)) { + $out = array($res['server']); + while($res = @odbc_data_source($this->connection, + SQL_FETCH_NEXT)) + { + $out[] = $res['server']; + } + return $out; + } else { + return $this->odbcRaiseError(); + } + break; + case 'tables': + case 'schema.tables': + $keep = 'TABLE'; + break; + case 'views': + $keep = 'VIEW'; + break; + default: + return null; + } + + /* + * Removing non-conforming items in the while loop rather than + * in the odbc_tables() call because some backends choke on this: + * odbc_tables($this->connection, '', '', '', 'TABLE') + */ + $res = @odbc_tables($this->connection); + if (!$res) { + return $this->odbcRaiseError(); + } + $out = array(); + while ($row = odbc_fetch_array($res)) { + if ($row['TABLE_TYPE'] != $keep) { + continue; + } + if ($type == 'schema.tables') { + $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME']; + } else { + $out[] = $row['TABLE_NAME']; + } + } + return $out; } // }}} diff --git a/thirdparty/pear/DB/pgsql.php b/thirdparty/pear/DB/pgsql.php old mode 100644 new mode 100755 index 2b011c9..039888f --- a/thirdparty/pear/DB/pgsql.php +++ b/thirdparty/pear/DB/pgsql.php @@ -1,141 +1,293 @@ | -// | Stig Bakken | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's pgsql extension + * for interacting with PostgreSQL databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Rui Hirokawa + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: pgsql.php,v 1.138 2007/09/21 13:40:41 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's PostgreSQL - * extension. + * The methods PEAR DB uses to interact with PHP's pgsql extension + * for interacting with PostgreSQL databases + * + * These methods overload the ones declared in DB_common. * - * @package DB - * @version $Id$ - * @category Database - * @author Rui Hirokawa - * @author Stig Bakken + * @category Database + * @package DB + * @author Rui Hirokawa + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_pgsql extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'pgsql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'pgsql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '4.3.0', + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => true, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); - var $transaction_opcount = 0; + + /** + * The DSN information for connecting to a database + * @var array + */ var $dsn = array(); - var $row = array(); - var $num_rows = array(); - var $affected = 0; + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ var $autocommit = true; - var $fetchmode = DB_FETCHMODE_ORDERED; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The number of rows affected by a data manipulation query + * @var integer + */ + var $affected = 0; + + /** + * The current row being looked at in fetchInto() + * @var array + * @access private + */ + var $row = array(); + + /** + * The number of rows in a given result set + * @var array + * @access private + */ + var $_num_rows = array(); + // }}} // {{{ constructor + /** + * This constructor calls $this->DB_common() + * + * @return void + */ function DB_pgsql() { $this->DB_common(); - $this->phptype = 'pgsql'; - $this->dbsyntax = 'pgsql'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => true, - 'limit' => 'alter' - ); - $this->errorcode_map = array( - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent + * PEAR DB's pgsql driver supports the following extra DSN options: + * + connect_timeout How many seconds to wait for a connection to + * be established. Available since PEAR DB 1.7.0. + * + new_link If set to true, causes subsequent calls to + * connect() to return a new connection link + * instead of the existing one. WARNING: this is + * not portable to other DBMS's. Available only + * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0. + * + options Command line options to be sent to the server. + * Available since PEAR DB 1.6.4. + * + service Specifies a service name in pg_service.conf that + * holds additional connection parameters. + * Available since PEAR DB 1.7.0. + * + sslmode How should SSL be used when connecting? Values: + * disable, allow, prefer or require. + * Available since PEAR DB 1.7.0. + * + tty This was used to specify where to send server + * debug output. Available since PEAR DB 1.6.4. * - * @return int DB_OK on success, a DB error code on failure. + * Example of connecting to a new link via a socket: + * + * require_once 'DB.php'; + * + * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true'; + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('pgsql')) { + if (!PEAR::loadExtension('pgsql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; - $protocol = $dsninfo['protocol'] ? $dsninfo['protocol'] : 'tcp'; - $connstr = ''; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp'; + $params = array(''); if ($protocol == 'tcp') { - if ($dsninfo['hostspec']) { - $connstr .= 'host=' . $dsninfo['hostspec']; + if ($dsn['hostspec']) { + $params[0] .= 'host=' . $dsn['hostspec']; } - if ($dsninfo['port']) { - $connstr .= ' port=' . $dsninfo['port']; + if ($dsn['port']) { + $params[0] .= ' port=' . $dsn['port']; } } elseif ($protocol == 'unix') { // Allow for pg socket in non-standard locations. - if ($dsninfo['socket']) { - $connstr .= 'host=' . $dsninfo['socket']; + if ($dsn['socket']) { + $params[0] .= 'host=' . $dsn['socket']; } - if ($dsninfo['port']) { - $connstr .= ' port=' . $dsninfo['port']; + if ($dsn['port']) { + $params[0] .= ' port=' . $dsn['port']; } } - - if ($dsninfo['database']) { - $connstr .= ' dbname=\'' . addslashes($dsninfo['database']) . '\''; + if ($dsn['database']) { + $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\''; + } + if ($dsn['username']) { + $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\''; + } + if ($dsn['password']) { + $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\''; } - if ($dsninfo['username']) { - $connstr .= ' user=\'' . addslashes($dsninfo['username']) . '\''; + if (!empty($dsn['options'])) { + $params[0] .= ' options=' . $dsn['options']; } - if ($dsninfo['password']) { - $connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\''; + if (!empty($dsn['tty'])) { + $params[0] .= ' tty=' . $dsn['tty']; } - if (!empty($dsninfo['options'])) { - $connstr .= ' options=' . $dsninfo['options']; + if (!empty($dsn['connect_timeout'])) { + $params[0] .= ' connect_timeout=' . $dsn['connect_timeout']; } - if (!empty($dsninfo['tty'])) { - $connstr .= ' tty=' . $dsninfo['tty']; + if (!empty($dsn['sslmode'])) { + $params[0] .= ' sslmode=' . $dsn['sslmode']; + } + if (!empty($dsn['service'])) { + $params[0] .= ' service=' . $dsn['service']; + } + + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + if (version_compare(phpversion(), '4.3.0', '>=')) { + $params[] = PGSQL_CONNECT_FORCE_NEW; + } } $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; $ini = ini_get('track_errors'); + $php_errormsg = ''; if ($ini) { - $conn = @$connect_function($connstr); + $this->connection = @call_user_func_array($connect_function, + $params); } else { - ini_set('track_errors', 1); - $conn = @$connect_function($connstr); - ini_set('track_errors', $ini); + @ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + @ini_set('track_errors', $ini); } - if ($conn == false) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, - null, null, strip_tags($php_errormsg)); + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); } - $this->connection = $conn; return DB_OK; } @@ -143,9 +295,9 @@ class DB_pgsql extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. + * Disconnects from the database server * - * @return bool true on success, false if not connected. + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -158,18 +310,17 @@ class DB_pgsql extends DB_common // {{{ simpleQuery() /** - * Send a query to PostgreSQL and return the results as a - * PostgreSQL resource identifier. + * Sends a query to the database server * - * @param $query the SQL query + * @param string the SQL query string * - * @return int returns a valid PostgreSQL result for successful SELECT - * queries, DB_OK for other successful queries. A DB error code - * is returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); if (!$this->autocommit && $ismanip) { @@ -185,25 +336,32 @@ class DB_pgsql extends DB_common if (!$result) { return $this->pgsqlRaiseError(); } - // Determine which queries that should return data, and which - // should return an error code only. + + /* + * Determine whether queries produce affected rows, result or nothing. + * + * This logic was introduced in version 1.1 of the file by ssb, + * though the regex has been modified slightly since then. + * + * PostgreSQL commands: + * ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, + * CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, + * GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, + * REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, + * UNLISTEN, UPDATE, VACUUM + */ if ($ismanip) { - $this->affected = @pg_cmdtuples($result); + $this->affected = @pg_affected_rows($result); return DB_OK; - } elseif (preg_match('/^\s*\(?\s*(SELECT(?!\s+INTO)|EXPLAIN|SHOW)\s/si', $query)) { - /* PostgreSQL commands: - ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, - CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, - GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, - REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, - UNLISTEN, UPDATE, VACUUM - */ + } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW)\s/si', + $query)) + { $this->row[(int)$result] = 0; // reset the row counter. - $numrows = $this->numrows($result); + $numrows = $this->numRows($result); if (is_object($numrows)) { return $numrows; } - $this->num_rows[(int)$result] = $numrows; + $this->_num_rows[(int)$result] = $numrows; $this->affected = 0; return $result; } else { @@ -230,69 +388,33 @@ class DB_pgsql extends DB_common } // }}} - // {{{ errorCode() - - /** - * Determine PEAR::DB error code from the database's text error message. - * - * @param string $errormsg error message returned from the database - * @return integer an error number from a DB error constant - */ - function errorCode($errormsg) - { - static $error_regexps; - if (!isset($error_regexps)) { - $error_regexps = array( - '/(([Rr]elation|[Ss]equence|[Tt]able)( [\"\'].*[\"\'])? does not exist|[Cc]lass ".+" not found)$/' => DB_ERROR_NOSUCHTABLE, - '/[Cc]olumn [\"\'].*[\"\'] .*does not exist/' => DB_ERROR_NOSUCHFIELD, - '/[Rr]elation [\"\'].*[\"\'] already exists|[Cc]annot insert a duplicate key into (a )?unique index.*/' => DB_ERROR_ALREADY_EXISTS, - '/(divide|division) by zero$/' => DB_ERROR_DIVZERO, - '/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER, - '/invalid input syntax for integer/' => DB_ERROR_INVALID_NUMBER, - '/ttribute [\"\'].*[\"\'] not found$|[Rr]elation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD, - '/parser: parse error at or near \"/' => DB_ERROR_SYNTAX, - '/syntax error at/' => DB_ERROR_SYNTAX, - '/permission denied/' => DB_ERROR_ACCESS_VIOLATION, - '/violates not-null constraint/' => DB_ERROR_CONSTRAINT_NOT_NULL, - '/violates [\w ]+ constraint/' => DB_ERROR_CONSTRAINT, - '/referential integrity violation/' => DB_ERROR_CONSTRAINT - ); - } - foreach ($error_regexps as $regexp => $code) { - if (preg_match($regexp, $errormsg)) { - return $code; - } - } - // Fall back to DB_ERROR if there was no mapping. - return DB_ERROR; - } - - // }}} // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { $result_int = (int)$result; $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int]; - if ($rownum >= $this->num_rows[$result_int]) { + if ($rownum >= $this->_num_rows[$result_int]) { return null; } if ($fetchmode & DB_FETCHMODE_ASSOC) { @@ -304,11 +426,7 @@ class DB_pgsql extends DB_common $arr = @pg_fetch_row($result, $rownum); } if (!$arr) { - $err = pg_errormessage($this->connection); - if (!$err) { - return null; - } - return $this->pgsqlRaiseError(); + return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); @@ -324,17 +442,23 @@ class DB_pgsql extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource * - * @param $result int PostgreSQL result identifier + * @return bool TRUE on success, FALSE if $result is invalid * - * @return bool true on success, false if $result is invalid + * @see DB_result::free() */ function freeResult($result) { if (is_resource($result)) { unset($this->row[(int)$result]); - unset($this->num_rows[(int)$result]); + unset($this->_num_rows[(int)$result]); $this->affected = 0; return @pg_freeresult($result); } @@ -348,72 +472,77 @@ class DB_pgsql extends DB_common * @deprecated Deprecated in release 1.6.0 * @internal */ - function quote($str) { + function quote($str) + { return $this->quoteSmart($str); } // }}} - // {{{ quoteSmart() + // {{{ quoteBoolean() /** - * Format input so it can be safely used in a query - * - * @param mixed $in data to be quoted + * Formats a boolean value for use within a query in a locale-independent + * manner. * - * @return mixed Submitted variable's type = returned value: - * + null = the string NULL - * + boolean = string TRUE or FALSE - * + integer or double = the unquoted number - * + other (including strings and numeric strings) = - * the data escaped according to MySQL's settings - * then encapsulated between single quotes - * - * @internal + * @param boolean the boolean value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. */ - function quoteSmart($in) - { - if (is_int($in) || is_double($in)) { - return $in; - } elseif (is_bool($in)) { - return $in ? 'TRUE' : 'FALSE'; - } elseif (is_null($in)) { - return 'NULL'; - } else { - return "'" . $this->escapeSimple($in) . "'"; - } + function quoteBoolean($boolean) { + return $boolean ? 'TRUE' : 'FALSE'; } - + // }}} // {{{ escapeSimple() /** - * Escape a string according to the current DBMS's standards - * - * PostgreSQL treats a backslash as an escape character, so they are - * removed. + * Escapes a string according to the current DBMS's standards * - * Not using pg_escape_string() yet because it requires PostgreSQL - * to be at version 7.2 or greater. + * {@internal PostgreSQL treats a backslash as an escape character, + * so they are escaped as well. * * @param string $str the string to be escaped * * @return string the escaped string * - * @internal + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 */ - function escapeSimple($str) { - return str_replace("'", "''", str_replace('\\', '\\\\', $str)); + function escapeSimple($str) + { + if (function_exists('pg_escape_string')) { + /* This fixes an undocumented BC break in PHP 5.2.0 which changed + * the prototype of pg_escape_string. I'm not thrilled about having + * to sniff the PHP version, quite frankly, but it's the only way + * to deal with the problem. Revision 1.331.2.13.2.10 on + * php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the + * record. */ + if (version_compare(PHP_VERSION, '5.2.0', '>=')) { + return pg_escape_string($this->connection, $str); + } else { + return pg_escape_string($str); + } + } else { + return str_replace("'", "''", str_replace('\\', '\\\\', $str)); + } } // }}} // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @param $result resource PostgreSQL result identifier + * @param resource $result PHP's query result resource * - * @return int the number of columns per row in $result + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() */ function numCols($result) { @@ -428,11 +557,17 @@ class DB_pgsql extends DB_common // {{{ numRows() /** - * Get the number of rows in a result set. + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource * - * @param $result resource PostgreSQL result identifier + * @return int the number of rows. A DB_Error object on failure. * - * @return int the number of rows in $result + * @see DB_result::numRows() */ function numRows($result) { @@ -444,24 +579,15 @@ class DB_pgsql extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @return int native PostgreSQL error code - */ - function errorNative() - { - return pg_errormessage($this->connection); - } - - // }}} // {{{ autoCommit() /** - * Enable/disable automatic commits + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { @@ -475,7 +601,9 @@ class DB_pgsql extends DB_common // {{{ commit() /** - * Commit the current transaction. + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @@ -495,7 +623,9 @@ class DB_pgsql extends DB_common // {{{ rollback() /** - * Roll back (undo) the current transaction. + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @@ -513,10 +643,11 @@ class DB_pgsql extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the last query. - * if the last query was a select, returns 0. + * Determines the number of rows affected by a data maniuplation query * - * @return int number of rows affected by the last query or DB_ERROR + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { @@ -531,13 +662,13 @@ class DB_pgsql extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_pgsql::createSequence(), DB_pgsql::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -545,7 +676,7 @@ class DB_pgsql extends DB_common $repeat = false; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); - $result =& $this->query("SELECT NEXTVAL('${seqname}')"); + $result = $this->query("SELECT NEXTVAL('${seqname}')"); $this->popErrorHandling(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { @@ -572,11 +703,14 @@ class DB_pgsql extends DB_common // {{{ createSequence() /** - * Create the sequence + * Creates a new sequence * - * @param string $seq_name the name of the sequence - * @return mixed DB_OK on success or DB error on error - * @access public + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_pgsql::nextID(), DB_pgsql::dropSequence() */ function createSequence($seq_name) { @@ -589,152 +723,191 @@ class DB_pgsql extends DB_common // {{{ dropSequence() /** - * Drop a sequence + * Deletes a sequence * - * @param string $seq_name the name of the sequence - * @return mixed DB_OK on success or DB error on error - * @access public + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_pgsql::nextID(), DB_pgsql::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP SEQUENCE ${seqname}"); + return $this->query('DROP SEQUENCE ' + . $this->getSequenceName($seq_name)); } // }}} // {{{ modifyLimitQuery() + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ function modifyLimitQuery($query, $from, $count, $params = array()) { - $query = $query . " LIMIT $count OFFSET $from"; - return $query; + return "$query LIMIT $count OFFSET $from"; } // }}} // {{{ pgsqlRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see errorNative() - * @see errorCode() - * @see DB_common::raiseError() + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_pgsql::errorNative(), DB_pgsql::errorCode() */ function pgsqlRaiseError($errno = null) { $native = $this->errorNative(); + if (!$native) { + $native = 'Database connection has been lost.'; + $errno = DB_ERROR_CONNECT_FAILED; + } if ($errno === null) { - $err = $this->errorCode($native); - } else { - $err = $errno; + $errno = $this->errorCode($native); } - return $this->raiseError($err, null, null, null, $native); + return $this->raiseError($errno, null, null, null, $native); } // }}} - // {{{ _pgFieldFlags() + // {{{ errorNative() /** - * Flags of a Field + * Gets the DBMS' native error message produced by the last query * - * @param int $resource PostgreSQL result identifier - * @param int $num_field the field number + * {@internal Error messages are used instead of error codes + * in order to support older versions of PostgreSQL.}} * - * @return string The flags of the field ("not_null", "default_value", - * "primary_key", "unique_key" and "multiple_key" - * are supported). The default value is passed - * through rawurlencode() in case there are spaces in it. - * @access private + * @return string the DBMS' error message */ - function _pgFieldFlags($resource, $num_field, $table_name) + function errorNative() { - $field_name = @pg_fieldname($resource, $num_field); + return @pg_errormessage($this->connection); + } - $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef - FROM pg_attribute f, pg_class tab, pg_type typ - WHERE tab.relname = typ.typname - AND typ.typrelid = f.attrelid - AND f.attname = '$field_name' - AND tab.relname = '$table_name'"); - if (@pg_numrows($result) > 0) { - $row = @pg_fetch_row($result, 0); - $flags = ($row[0] == 't') ? 'not_null ' : ''; + // }}} + // {{{ errorCode() - if ($row[1] == 't') { - $result = @pg_exec($this->connection, "SELECT a.adsrc - FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a - WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid - AND f.attrelid = a.adrelid AND f.attname = '$field_name' - AND tab.relname = '$table_name' AND f.attnum = a.adnum"); - $row = @pg_fetch_row($result, 0); - $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]); - $flags .= 'default_' . rawurlencode($num) . ' '; - } - } else { - $flags = ''; + /** + * Determines PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/column .* (of relation .*)?does not exist/i' + => DB_ERROR_NOSUCHFIELD, + '/(relation|sequence|table).*does not exist|class .* not found/i' + => DB_ERROR_NOSUCHTABLE, + '/index .* does not exist/' + => DB_ERROR_NOT_FOUND, + '/relation .* already exists/i' + => DB_ERROR_ALREADY_EXISTS, + '/(divide|division) by zero$/i' + => DB_ERROR_DIVZERO, + '/pg_atoi: error in .*: can\'t parse /i' + => DB_ERROR_INVALID_NUMBER, + '/invalid input syntax for( type)? (integer|numeric)/i' + => DB_ERROR_INVALID_NUMBER, + '/value .* is out of range for type \w*int/i' + => DB_ERROR_INVALID_NUMBER, + '/integer out of range/i' + => DB_ERROR_INVALID_NUMBER, + '/value too long for type character/i' + => DB_ERROR_INVALID, + '/attribute .* not found|relation .* does not have attribute/i' + => DB_ERROR_NOSUCHFIELD, + '/column .* specified in USING clause does not exist in (left|right) table/i' + => DB_ERROR_NOSUCHFIELD, + '/parser: parse error at or near/i' + => DB_ERROR_SYNTAX, + '/syntax error at/' + => DB_ERROR_SYNTAX, + '/column reference .* is ambiguous/i' + => DB_ERROR_SYNTAX, + '/permission denied/' + => DB_ERROR_ACCESS_VIOLATION, + '/violates not-null constraint/' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/violates [\w ]+ constraint/' + => DB_ERROR_CONSTRAINT, + '/referential integrity violation/' + => DB_ERROR_CONSTRAINT, + '/more expressions than target columns/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + ); } - $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey - FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i - WHERE tab.relname = typ.typname - AND typ.typrelid = f.attrelid - AND f.attrelid = i.indrelid - AND f.attname = '$field_name' - AND tab.relname = '$table_name'"); - $count = @pg_numrows($result); - - for ($i = 0; $i < $count ; $i++) { - $row = @pg_fetch_row($result, $i); - $keys = explode(' ', $row[2]); - - if (in_array($num_field + 1, $keys)) { - $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : ''; - $flags .= ($row[1] == 't') ? 'primary_key ' : ''; - if (count($keys) > 1) - $flags .= 'multiple_key '; + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; } } - - return trim($flags); + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; } // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { - if (isset($result->result)) { - /* - * Probably received a result object. - * Extract the result resource identifier. - */ - $id = $result->result; - $got_string = false; - } elseif (is_string($result)) { + if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0"); $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; } else { /* * Probably received a result resource identifier. @@ -756,34 +929,27 @@ class DB_pgsql extends DB_common } $count = @pg_numfields($id); + $res = array(); - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { + if ($mode) { + $res['num_fields'] = $count; + } - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func(@pg_fieldname($id, $i)); - $res[$i]['type'] = @pg_fieldtype($id, $i); - $res[$i]['len'] = @pg_fieldsize($id, $i); - $res[$i]['flags'] = $got_string ? $this->_pgFieldflags($id, $i, $result) : ''; + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func(@pg_fieldname($id, $i)), + 'type' => @pg_fieldtype($id, $i), + 'len' => @pg_fieldsize($id, $i), + 'flags' => $got_string + ? $this->_pgFieldFlags($id, $i, $result) + : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; } - - } else { // full - $res['num_fields']= $count; - - for ($i=0; $i<$count; $i++) { - $res[$i]['table'] = $got_string ? $case_func($result) : ''; - $res[$i]['name'] = $case_func(@pg_fieldname($id, $i)); - $res[$i]['type'] = @pg_fieldtype($id, $i); - $res[$i]['len'] = @pg_fieldsize($id, $i); - $res[$i]['flags'] = $got_string ? $this->_pgFieldFlags($id, $i, $result) : ''; - - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -795,39 +961,142 @@ class DB_pgsql extends DB_common } // }}} - // {{{ getTablesQuery() + // {{{ _pgFieldFlags() + + /** + * Get a column's flags + * + * Supports "not_null", "default_value", "primary_key", "unique_key" + * and "multiple_key". The default value is passed through + * rawurlencode() in case there are spaces in it. + * + * @param int $resource the PostgreSQL result identifier + * @param int $num_field the field number + * + * @return string the flags + * + * @access private + */ + function _pgFieldFlags($resource, $num_field, $table_name) + { + $field_name = @pg_fieldname($resource, $num_field); + + // Check if there's a schema in $table_name and update things + // accordingly. + $from = 'pg_attribute f, pg_class tab, pg_type typ'; + if (strpos($table_name, '.') !== false) { + $from .= ', pg_namespace nsp'; + list($schema, $table) = explode('.', $table_name); + $tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'"; + } else { + $tableWhere = "tab.relname = '$table_name'"; + } + + $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef + FROM $from + WHERE tab.relname = typ.typname + AND typ.typrelid = f.attrelid + AND f.attname = '$field_name' + AND $tableWhere"); + if (@pg_numrows($result) > 0) { + $row = @pg_fetch_row($result, 0); + $flags = ($row[0] == 't') ? 'not_null ' : ''; + + if ($row[1] == 't') { + $result = @pg_exec($this->connection, "SELECT a.adsrc + FROM $from, pg_attrdef a + WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid + AND f.attrelid = a.adrelid AND f.attname = '$field_name' + AND $tableWhere AND f.attnum = a.adnum"); + $row = @pg_fetch_row($result, 0); + $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]); + $flags .= 'default_' . rawurlencode($num) . ' '; + } + } else { + $flags = ''; + } + $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey + FROM $from, pg_index i + WHERE tab.relname = typ.typname + AND typ.typrelid = f.attrelid + AND f.attrelid = i.indrelid + AND f.attname = '$field_name' + AND $tableWhere"); + $count = @pg_numrows($result); + + for ($i = 0; $i < $count ; $i++) { + $row = @pg_fetch_row($result, $i); + $keys = explode(' ', $row[2]); + + if (in_array($num_field + 1, $keys)) { + $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : ''; + $flags .= ($row[1] == 't') ? 'primary_key ' : ''; + if (count($keys) > 1) + $flags .= 'multiple_key '; + } + } + + return trim($flags); + } + + // }}} + // {{{ getSpecialQuery() /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': - return "SELECT c.relname as \"Name\" - FROM pg_class c, pg_user u - WHERE c.relowner = u.usesysid AND c.relkind = 'r' - AND not exists (select 1 from pg_views where viewname = c.relname) - AND c.relname !~ '^(pg_|sql_)' - UNION - SELECT c.relname as \"Name\" - FROM pg_class c - WHERE c.relkind = 'r' - AND not exists (select 1 from pg_views where viewname = c.relname) - AND not exists (select 1 from pg_user where usesysid = c.relowner) - AND c.relname !~ '^pg_'"; + return 'SELECT c.relname AS "Name"' + . ' FROM pg_class c, pg_user u' + . ' WHERE c.relowner = u.usesysid' + . " AND c.relkind = 'r'" + . ' AND NOT EXISTS' + . ' (SELECT 1 FROM pg_views' + . ' WHERE viewname = c.relname)' + . " AND c.relname !~ '^(pg_|sql_)'" + . ' UNION' + . ' SELECT c.relname AS "Name"' + . ' FROM pg_class c' + . " WHERE c.relkind = 'r'" + . ' AND NOT EXISTS' + . ' (SELECT 1 FROM pg_views' + . ' WHERE viewname = c.relname)' + . ' AND NOT EXISTS' + . ' (SELECT 1 FROM pg_user' + . ' WHERE usesysid = c.relowner)' + . " AND c.relname !~ '^pg_'"; + case 'schema.tables': + return "SELECT schemaname || '.' || tablename" + . ' AS "Name"' + . ' FROM pg_catalog.pg_tables' + . ' WHERE schemaname NOT IN' + . " ('pg_catalog', 'information_schema', 'pg_toast')"; + case 'schema.views': + return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname" + . " NOT IN ('information_schema', 'pg_catalog')"; case 'views': // Table cols: viewname | viewowner | definition - return 'SELECT viewname FROM pg_views'; + return 'SELECT viewname from pg_views WHERE schemaname' + . " NOT IN ('information_schema', 'pg_catalog')"; case 'users': // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil return 'SELECT usename FROM pg_user'; case 'databases': return 'SELECT datname FROM pg_database'; case 'functions': - return 'SELECT proname FROM pg_proc'; + case 'procedures': + return 'SELECT proname FROM pg_proc WHERE proowner <> 1'; default: return null; } diff --git a/thirdparty/pear/DB/sqlite.php b/thirdparty/pear/DB/sqlite.php old mode 100644 new mode 100755 index ef5535b..bf2acec --- a/thirdparty/pear/DB/sqlite.php +++ b/thirdparty/pear/DB/sqlite.php @@ -1,152 +1,251 @@ | -// | Mika Tuupola | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * The PEAR DB driver for PHP's sqlite extension + * for interacting with SQLite databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Urs Gehrig + * @author Mika Tuupola + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 + * @version CVS: $Id: sqlite.php,v 1.117 2007/09/21 14:23:28 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for the SQLite - * PECL extension. + * The methods PEAR DB uses to interact with PHP's sqlite extension + * for interacting with SQLite databases + * + * These methods overload the ones declared in DB_common. + * + * NOTICE: This driver needs PHP's track_errors ini setting to be on. + * It is automatically turned on when connecting to the database. + * Make sure your scripts don't turn it off. * - * @package DB - * @version $Id$ - * @category Database - * @author Urs Gehrig - * @author Mika Tuupola + * @category Database + * @package DB + * @author Urs Gehrig + * @author Mika Tuupola + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_sqlite extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'sqlite'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sqlite'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * + * {@internal Error codes according to sqlite_exec. See the online + * manual at http://sqlite.org/c_interface.html for info. + * This error handling based on sqlite_exec is not yet implemented.}} + * + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * SQLite data types + * + * @link http://www.sqlite.org/datatypes.html + * + * @var array + */ + var $keywords = array ( + 'BLOB' => '', + 'BOOLEAN' => '', + 'CHARACTER' => '', + 'CLOB' => '', + 'FLOAT' => '', + 'INTEGER' => '', + 'KEY' => '', + 'NATIONAL' => '', + 'NUMERIC' => '', + 'NVARCHAR' => '', + 'PRIMARY' => '', + 'TEXT' => '', + 'TIMESTAMP' => '', + 'UNIQUE' => '', + 'VARCHAR' => '', + 'VARYING' => '', + ); + + /** + * The most recent error message from $php_errormsg + * @var string + * @access private + */ var $_lasterror = ''; + // }}} // {{{ constructor /** - * Constructor for this class. - * - * Error codes according to sqlite_exec. Error Codes specification is - * in the {@link http://sqlite.org/c_interface.html online manual}. + * This constructor calls $this->DB_common() * - * This errorhandling based on sqlite_exec is not yet implemented. - * - * @access public + * @return void */ function DB_sqlite() { - $this->DB_common(); - $this->phptype = 'sqlite'; - $this->dbsyntax = 'sqlite'; - $this->features = array ( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => false, - 'limit' => 'alter' - ); - - // SQLite data types, http://www.sqlite.org/datatypes.html - $this->keywords = array ( - 'BLOB' => '', - 'BOOLEAN' => '', - 'CHARACTER' => '', - 'CLOB' => '', - 'FLOAT' => '', - 'INTEGER' => '', - 'KEY' => '', - 'NATIONAL' => '', - 'NUMERIC' => '', - 'NVARCHAR' => '', - 'PRIMARY' => '', - 'TEXT' => '', - 'TIMESTAMP' => '', - 'UNIQUE' => '', - 'VARCHAR' => '', - 'VARYING' => '' - ); - $this->errorcode_map = array( - ); } // }}} // {{{ connect() /** - * Connect to a database represented by a file. - * - * @param $dsn the data source name; the file is taken as - * database; "sqlite://root:@host/test.db?mode=0644" - * @param $persistent (optional) whether the connection should - * be persistent - * @access public - * @return int DB_OK on success, a DB error on failure + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's sqlite driver supports the following extra DSN options: + * + mode The permissions for the database file, in four digit + * chmod octal format (eg "0600"). + * + * Example of connecting to a database in read-only mode: + * + * require_once 'DB.php'; + * + * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400'; + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db = DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('sqlite')) { + if (!PEAR::loadExtension('sqlite')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } - if ($dsninfo['database']) { - if (!file_exists($dsninfo['database'])) { - if (!touch($dsninfo['database'])) { + if (!$dsn['database']) { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + + if ($dsn['database'] !== ':memory:') { + if (!file_exists($dsn['database'])) { + if (!touch($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); } - if (!isset($dsninfo['mode']) || - !is_numeric($dsninfo['mode'])) + if (!isset($dsn['mode']) || + !is_numeric($dsn['mode'])) { $mode = 0644; } else { - $mode = octdec($dsninfo['mode']); + $mode = octdec($dsn['mode']); } - if (!chmod($dsninfo['database'], $mode)) { + if (!chmod($dsn['database'], $mode)) { return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); } - if (!file_exists($dsninfo['database'])) { + if (!file_exists($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); } } - if (!is_file($dsninfo['database'])) { + if (!is_file($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_INVALID); } - if (!is_readable($dsninfo['database'])) { + if (!is_readable($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); } - } else { - return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); } $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; - if (!($conn = @$connect_function($dsninfo['database']))) { - return $this->sqliteRaiseError(DB_ERROR_NODBSELECTED); - } - $this->connection = $conn; + // track_errors must remain on for simpleQuery() + @ini_set('track_errors', 1); + $php_errormsg = ''; + + if (!$this->connection = @$connect_function($dsn['database'])) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + $php_errormsg); + } return DB_OK; } @@ -154,11 +253,9 @@ class DB_sqlite extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. + * Disconnects from the database server * - * @access public - * @return bool true on success, false if not connected. - * @todo fix return values + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -171,36 +268,40 @@ class DB_sqlite extends DB_common // {{{ simpleQuery() /** - * Send a query to SQLite and returns the results as a SQLite resource - * identifier. - * - * @param the SQL query - * @access public - * @return mixed returns a valid SQLite result for successful SELECT - * queries, DB_OK for other successful queries. A DB error is - * returned on failure. + * Sends a query to the database server + * + * NOTICE: This method needs PHP's track_errors ini setting to be on. + * It is automatically turned on when connecting to the database. + * Make sure your scripts don't turn it off. + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; - $query = $this->_modifyQuery($query); - ini_set('track_errors', true); + $query = $this->modifyQuery($query); + + $php_errormsg = ''; + $result = @sqlite_query($query, $this->connection); - ini_restore('track_errors'); - $this->_lasterror = isset($php_errormsg) ? $php_errormsg : ''; + $this->_lasterror = $php_errormsg ? $php_errormsg : ''; + $this->result = $result; if (!$this->result) { return $this->sqliteRaiseError(null); } - /* sqlite_query() seems to allways return a resource */ - /* so cant use that. Using $ismanip instead */ + // sqlite_query() seems to allways return a resource + // so cant use that. Using $ismanip instead if (!$ismanip) { $numRows = $this->numRows($result); - - /* if numRows() returned PEAR_Error */ if (is_object($numRows)) { + // we've got PEAR_Error return $numRows; } return $result; @@ -212,11 +313,11 @@ class DB_sqlite extends DB_common // {{{ nextResult() /** - * Move the internal sqlite result pointer to the next available result. + * Move the internal sqlite result pointer to the next available result * - * @param a valid sqlite result resource - * @access public - * @return true if a result is available otherwise return false + * @param resource $result the valid sqlite result resource + * + * @return bool true if a result is available otherwise return false */ function nextResult($result) { @@ -227,24 +328,26 @@ class DB_sqlite extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@sqlite_seek($this->result, $rownum)) { @@ -256,11 +359,20 @@ class DB_sqlite extends DB_common if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } + + /* Remove extraneous " characters from the fields in the result. + * Fixes bug #11716. */ + if (is_array($arr) && count($arr) > 0) { + $strippedArr = array(); + foreach ($arr as $field => $value) { + $strippedArr[trim($field, '"')] = $value; + } + $arr = $strippedArr; + } } else { $arr = @sqlite_fetch_array($result, SQLITE_NUM); } if (!$arr) { - /* See: http://bugs.php.net/bug.php?id=22328 */ return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { @@ -281,11 +393,17 @@ class DB_sqlite extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource * - * @param $result SQLite result identifier - * @access public - * @return bool true on success, false if $result is invalid + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() */ function freeResult(&$result) { @@ -301,9 +419,17 @@ class DB_sqlite extends DB_common // {{{ numCols() /** - * Gets the number of columns in a result set. + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @return number of columns in a result set + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() */ function numCols($result) { @@ -318,15 +444,23 @@ class DB_sqlite extends DB_common // {{{ numRows() /** - * Gets the number of rows affected by a query. + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. * - * @return number of rows affected by the last query + * @see DB_result::numRows() */ function numRows($result) { $rows = @sqlite_num_rows($result); - if (!is_integer($rows)) { - return $this->raiseError(); + if ($rows === null) { + return $this->sqliteRaiseError(); } return $rows; } @@ -335,9 +469,11 @@ class DB_sqlite extends DB_common // {{{ affected() /** - * Gets the number of rows affected by a query. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return number of rows affected by the last query + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { @@ -345,56 +481,6 @@ class DB_sqlite extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the native error string of the last error (if any) that - * occured on the current connection. - * - * This is used to retrieve more meaningfull error messages DB_pgsql - * way since sqlite_last_error() does not provide adequate info. - * - * @return string native SQLite error message - */ - function errorNative() - { - return($this->_lasterror); - } - - // }}} - // {{{ errorCode() - - /** - * Determine PEAR::DB error code from the database's text error message. - * - * @param string $errormsg error message returned from the database - * @return integer an error number from a DB error constant - */ - function errorCode($errormsg) - { - static $error_regexps; - if (!isset($error_regexps)) { - $error_regexps = array( - '/^no such table:/' => DB_ERROR_NOSUCHTABLE, - '/^table .* already exists$/' => DB_ERROR_ALREADY_EXISTS, - '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, - '/is not unique/' => DB_ERROR_CONSTRAINT, - '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, - '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, - '/^no such column:/' => DB_ERROR_NOSUCHFIELD, - '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX - ); - } - foreach ($error_regexps as $regexp => $code) { - if (preg_match($regexp, $errormsg)) { - return $code; - } - } - // Fall back to DB_ERROR if there was no mapping. - return DB_ERROR; - } - - // }}} // {{{ dropSequence() /** @@ -402,16 +488,14 @@ class DB_sqlite extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_sqlite::nextID(), DB_sqlite::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP TABLE $seqname"); + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } /** @@ -419,12 +503,10 @@ class DB_sqlite extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_sqlite::nextID(), DB_sqlite::dropSequence() */ function createSequence($seq_name) { @@ -453,13 +535,13 @@ class DB_sqlite extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_sqlite::createSequence(), DB_sqlite::dropSequence() */ function nextId($seq_name, $ondemand = true) { @@ -491,94 +573,19 @@ class DB_sqlite extends DB_common } // }}} - // {{{ getSpecialQuery() - - /** - * Returns the query needed to get some backend info. - * - * Refer to the online manual at http://sqlite.org/sqlite.html. - * - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string - */ - function getSpecialQuery($type, $args=array()) - { - if (!is_array($args)) - return $this->raiseError('no key specified', null, null, null, - 'Argument has to be an array.'); - switch (strtolower($type)) { - case 'master': - return 'SELECT * FROM sqlite_master;'; - case 'tables': - return "SELECT name FROM sqlite_master WHERE type='table' " - . 'UNION ALL SELECT name FROM sqlite_temp_master ' - . "WHERE type='table' ORDER BY name;"; - case 'schema': - return 'SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL ' - . 'SELECT * FROM sqlite_temp_master) ' - . "WHERE type!='meta' ORDER BY tbl_name, type DESC, name;"; - case 'schemax': - case 'schema_x': - /* - * Use like: - * $res = $db->query($db->getSpecialQuery('schema_x', array('table' => 'table3'))); - */ - return 'SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL ' - . 'SELECT * FROM sqlite_temp_master) ' - . "WHERE tbl_name LIKE '{$args['table']}' AND type!='meta' " - . 'ORDER BY type DESC, name;'; - case 'alter': - /* - * SQLite does not support ALTER TABLE; this is a helper query - * to handle this. 'table' represents the table name, 'rows' - * the news rows to create, 'save' the row(s) to keep _with_ - * the data. - * - * Use like: - * $args = array( - * 'table' => $table, - * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", - * 'save' => "NULL, titel, content, datetime" - * ); - * $res = $db->query( $db->getSpecialQuery('alter', $args)); - */ - $rows = strtr($args['rows'], $this->keywords); - - $q = array( - 'BEGIN TRANSACTION', - "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", - "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", - "DROP TABLE {$args['table']}", - "CREATE TABLE {$args['table']} ({$args['rows']})", - "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", - "DROP TABLE {$args['table']}_backup", - 'COMMIT', - ); - - // This is a dirty hack, since the above query will no get executed with a single - // query call; so here the query method will be called directly and return a select instead. - foreach ($q as $query) { - $this->query($query); - } - return "SELECT * FROM {$args['table']};"; - default: - return null; - } - } - - // }}} // {{{ getDbFileStats() /** - * Get the file stats for the current database. + * Get the file stats for the current database * * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, * atime, mtime, ctime, blksize, blocks or a numeric key between * 0 and 12. * - * @param string $arg Array key for stats() - * @return mixed array on an unspecified key, integer on a passed arg and - * false at a stats error. + * @param string $arg the array key for stats() + * + * @return mixed an array on an unspecified key, integer on a passed + * arg and false at a stats error */ function getDbFileStats($arg = '') { @@ -604,7 +611,7 @@ class DB_sqlite extends DB_common // {{{ escapeSimple() /** - * Escape a string according to the current DBMS's standards + * Escapes a string according to the current DBMS's standards * * In SQLite, this makes things safe for inserts/updates, but may * cause problems when performing text comparisons against columns @@ -615,35 +622,56 @@ class DB_sqlite extends DB_common * * @return string the escaped string * - * @since 1.6.1 + * @since Method available since Release 1.6.1 * @see DB_common::escapeSimple() - * @internal */ - function escapeSimple($str) { + function escapeSimple($str) + { return @sqlite_escape_string($str); } // }}} // {{{ modifyLimitQuery() + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ function modifyLimitQuery($query, $from, $count, $params = array()) { - $query = $query . " LIMIT $count OFFSET $from"; - return $query; + return "$query LIMIT $count OFFSET $from"; } // }}} // {{{ modifyQuery() /** - * "DELETE FROM table" gives 0 affected rows in SQLite. + * Changes a query string for various DBMS specific reasons + * + * This little hack lets you know how many rows were deleted + * when running a "DELETE FROM table" query. Only implemented + * if the DB_PORTABILITY_DELETE_COUNT portability option is on. + * + * @param string $query the query string to modify * - * This little hack lets you know how many rows were deleted. + * @return string the modified query string * - * @param string $query The SQL query string - * @return string The SQL query string + * @access protected + * @see DB_common::setOption() */ - function _modifyQuery($query) + function modifyQuery($query) { if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { @@ -658,19 +686,19 @@ class DB_sqlite extends DB_common // {{{ sqliteRaiseError() /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. - * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see errorNative() - * @see errorCode() - * @see DB_common::raiseError() + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_sqlite::errorNative(), DB_sqlite::errorCode() */ function sqliteRaiseError($errno = null) { - $native = $this->errorNative(); if ($errno === null) { $errno = $this->errorCode($native); @@ -683,6 +711,242 @@ class DB_sqlite extends DB_common } // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * {@internal This is used to retrieve more meaningfull error messages + * because sqlite_last_error() does not provide adequate info.}} + * + * @return string the DBMS' error message + */ + function errorNative() + { + return $this->_lasterror; + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message + * + * @param string $errormsg the error message returned from the database + * + * @return integer the DB error number + */ + function errorCode($errormsg) + { + static $error_regexps; + + // PHP 5.2+ prepends the function name to $php_errormsg, so we need + // this hack to work around it, per bug #9599. + $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg); + + if (!isset($error_regexps)) { + $error_regexps = array( + '/^no such table:/' => DB_ERROR_NOSUCHTABLE, + '/^no such index:/' => DB_ERROR_NOT_FOUND, + '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS, + '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, + '/is not unique/' => DB_ERROR_CONSTRAINT, + '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT, + '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, + '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^no such column:/' => DB_ERROR_NOSUCHFIELD, + '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD, + '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX, + '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table + * + * @param string $result a string containing the name of a table + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @sqlite_array_query($this->connection, + "PRAGMA table_info('$result');", + SQLITE_ASSOC); + $got_string = true; + } else { + $this->last_query = ''; + return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null, + 'This DBMS can not obtain tableInfo' . + ' from result sets'); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = count($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + if (strpos($id[$i]['type'], '(') !== false) { + $bits = explode('(', $id[$i]['type']); + $type = $bits[0]; + $len = rtrim($bits[1],')'); + } else { + $type = $id[$i]['type']; + $len = 0; + } + + $flags = ''; + if ($id[$i]['pk']) { + $flags .= 'primary_key '; + } + if ($id[$i]['notnull']) { + $flags .= 'not_null '; + } + if ($id[$i]['dflt_value'] !== null) { + $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']); + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($result), + 'name' => $case_func($id[$i]['name']), + 'type' => $type, + 'len' => $len, + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * @param array $args SQLITE DRIVER ONLY: a private array of arguments + * used by the getSpecialQuery(). Do not use + * this directly. + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type, $args = array()) + { + if (!is_array($args)) { + return $this->raiseError('no key specified', null, null, null, + 'Argument has to be an array.'); + } + + switch ($type) { + case 'master': + return 'SELECT * FROM sqlite_master;'; + case 'tables': + return "SELECT name FROM sqlite_master WHERE type='table' " + . 'UNION ALL SELECT name FROM sqlite_temp_master ' + . "WHERE type='table' ORDER BY name;"; + case 'schema': + return 'SELECT sql FROM (SELECT * FROM sqlite_master ' + . 'UNION ALL SELECT * FROM sqlite_temp_master) ' + . "WHERE type!='meta' " + . 'ORDER BY tbl_name, type DESC, name;'; + case 'schemax': + case 'schema_x': + /* + * Use like: + * $res = $db->query($db->getSpecialQuery('schema_x', + * array('table' => 'table3'))); + */ + return 'SELECT sql FROM (SELECT * FROM sqlite_master ' + . 'UNION ALL SELECT * FROM sqlite_temp_master) ' + . "WHERE tbl_name LIKE '{$args['table']}' " + . "AND type!='meta' " + . 'ORDER BY type DESC, name;'; + case 'alter': + /* + * SQLite does not support ALTER TABLE; this is a helper query + * to handle this. 'table' represents the table name, 'rows' + * the news rows to create, 'save' the row(s) to keep _with_ + * the data. + * + * Use like: + * $args = array( + * 'table' => $table, + * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", + * 'save' => "NULL, titel, content, datetime" + * ); + * $res = $db->query( $db->getSpecialQuery('alter', $args)); + */ + $rows = strtr($args['rows'], $this->keywords); + + $q = array( + 'BEGIN TRANSACTION', + "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", + "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", + "DROP TABLE {$args['table']}", + "CREATE TABLE {$args['table']} ({$args['rows']})", + "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", + "DROP TABLE {$args['table']}_backup", + 'COMMIT', + ); + + /* + * This is a dirty hack, since the above query will not get + * executed with a single query call so here the query method + * will be called directly and return a select instead. + */ + foreach ($q as $query) { + $this->query($query); + } + return "SELECT * FROM {$args['table']};"; + default: + return null; + } + } + + // }}} } /* diff --git a/thirdparty/pear/DB/storage.php b/thirdparty/pear/DB/storage.php old mode 100644 new mode 100755 index bc36533..30762e8 --- a/thirdparty/pear/DB/storage.php +++ b/thirdparty/pear/DB/storage.php @@ -1,36 +1,45 @@ | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ + +/** + * Provides an object interface to a table row + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: storage.php,v 1.24 2007/08/12 05:27:25 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB class so it can be extended from + */ require_once 'DB.php'; /** - * Provides an object interface to a table row. + * Provides an object interface to a table row * * It lets you add, delete and change rows using objects rather than SQL * statements. * - * @package DB - * @version $Id$ - * @category Database - * @author Stig Bakken + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_storage extends PEAR { @@ -284,7 +293,7 @@ class DB_storage extends PEAR function &create($table, &$data) { $classname = strtolower(get_class($this)); - $obj =& new $classname($table); + $obj = new $classname($table); foreach ($data as $name => $value) { $obj->_properties[$name] = true; $obj->$name = &$value; @@ -436,6 +445,8 @@ class DB_storage extends PEAR */ function store() { + $params = array(); + $vars = array(); foreach ($this->_changes as $name => $foo) { $params[] = &$this->$name; $vars[] = $name . ' = ?'; diff --git a/thirdparty/pear/DB/sybase.php b/thirdparty/pear/DB/sybase.php old mode 100644 new mode 100755 index 93e4e79..bb79c78 --- a/thirdparty/pear/DB/sybase.php +++ b/thirdparty/pear/DB/sybase.php @@ -1,127 +1,221 @@ | -// | Antnio Carlos Venncio Jnior | -// | Maintainer: Daniel Convissor | -// +----------------------------------------------------------------------+ -// -// $Id$ - - -// TODO -// - This driver may fail with multiple connections under the same -// user/pass/host and different databases +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * The PEAR DB driver for PHP's sybase extension + * for interacting with Sybase databases + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Antnio Carlos Venncio Jnior + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: sybase.php,v 1.87 2007/09/21 13:40:42 aharvey Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ require_once 'DB/common.php'; /** - * Database independent query interface definition for PHP's Sybase - * extension. + * The methods PEAR DB uses to interact with PHP's sybase extension + * for interacting with Sybase databases + * + * These methods overload the ones declared in DB_common. + * + * WARNING: This driver may fail with multiple connections under the + * same user/pass/host and different databases. * - * @package DB - * @version $Id$ - * @category Database - * @author Sterling Hughes - * @author Antnio Carlos Venncio Jnior + * @category Database + * @package DB + * @author Sterling Hughes + * @author Antnio Carlos Venncio Jnior + * @author Daniel Convissor + * @copyright 1997-2007 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 1.7.13 + * @link http://pear.php.net/package/DB */ class DB_sybase extends DB_common { // {{{ properties + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'sybase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sybase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); - var $transaction_opcount = 0; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ var $autocommit = true; + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; + + // }}} // {{{ constructor /** - * DB_sybase constructor. + * This constructor calls $this->DB_common() * - * @access public + * @return void */ function DB_sybase() { $this->DB_common(); - $this->phptype = 'sybase'; - $this->dbsyntax = 'sybase'; - $this->features = array( - 'prepare' => false, - 'pconnect' => true, - 'transactions' => false, - 'limit' => 'emulate' - ); - $this->errorcode_map = array( - ); } // }}} // {{{ connect() /** - * Connect to a database and log in as the specified user. + * Connect to the database server, log in and open the database * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent - * @access public - * @return int DB_OK on success, a DB error on failure + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's sybase driver supports the following extra DSN options: + * + appname The application name to use on this connection. + * Available since PEAR DB 1.7.0. + * + charset The character set to use on this connection. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. */ - function connect($dsninfo, $persistent = false) + function connect($dsn, $persistent = false) { - if (!DB::assertExtension('sybase') && - !DB::assertExtension('sybase_ct')) + if (!PEAR::loadExtension('sybase') && + !PEAR::loadExtension('sybase_ct')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } - $this->dsn = $dsninfo; + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; + $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false; + $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false; + $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false; - $interface = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; - $dsninfo['password'] = !empty($dsninfo['password']) ? $dsninfo['password'] : false; - $dsninfo['charset'] = isset($dsninfo['charset']) ? $dsninfo['charset'] : false; - $dsninfo['appname'] = isset($dsninfo['appname']) ? $dsninfo['appname'] : false; - - if ($interface && $dsninfo['username']) { - $conn = @$connect_function($interface, $dsninfo['username'], - $dsninfo['password'], - $dsninfo['charset'], - $dsninfo['appname']); + + if ($dsn['username']) { + $this->connection = @$connect_function($dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['charset'], + $dsn['appname']); } else { - $conn = false; + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'The DSN did not contain a username.'); } - if (!$conn) { - return $this->raiseError(DB_ERROR_CONNECT_FAILED); + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + @sybase_get_last_message()); } - if ($dsninfo['database']) { - if (!@sybase_select_db($dsninfo['database'], $conn)) { - return $this->raiseError(DB_ERROR_NODBSELECTED, null, - null, null, @sybase_get_last_message()); + if ($dsn['database']) { + if (!@sybase_select_db($dsn['database'], $this->connection)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + @sybase_get_last_message()); } - $this->_db = $dsninfo['database']; + $this->_db = $dsn['database']; } - $this->connection = $conn; return DB_OK; } @@ -129,11 +223,9 @@ class DB_sybase extends DB_common // {{{ disconnect() /** - * Log out and disconnect from the database. - * - * @access public + * Disconnects from the database server * - * @return bool true on success, false if not connected. + * @return bool TRUE on success, FALSE on failure */ function disconnect() { @@ -143,110 +235,22 @@ class DB_sybase extends DB_common } // }}} - // {{{ errorNative() - - /** - * Get the last server error messge (if any) - * - * @return string sybase last error message - */ - function errorNative() - { - return @sybase_get_last_message(); - } - - // }}} - // {{{ errorCode() - - /** - * Determine PEAR::DB error code from the database's text error message. - * - * @param string $errormsg error message returned from the database - * @return integer an error number from a DB error constant - */ - function errorCode($errormsg) - { - static $error_regexps; - if (!isset($error_regexps)) { - $error_regexps = array( - '/Incorrect syntax near/' - => DB_ERROR_SYNTAX, - '/^Unclosed quote before the character string [\"\'].*[\"\']\./' - => DB_ERROR_SYNTAX, - '/Implicit conversion from datatype [\"\'].+[\"\'] to [\"\'].+[\"\'] is not allowed\./' - => DB_ERROR_INVALID_NUMBER, - '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' - => DB_ERROR_NOSUCHTABLE, - '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' - => DB_ERROR_ACCESS_VIOLATION, - '/^.+ permission denied on object .+, database .+, owner .+/' - => DB_ERROR_ACCESS_VIOLATION, - '/^.* permission denied, database .+, owner .+/' - => DB_ERROR_ACCESS_VIOLATION, - '/[^.*] not found\./' - => DB_ERROR_NOSUCHTABLE, - '/There is already an object named/' - => DB_ERROR_ALREADY_EXISTS, - '/Invalid column name/' - => DB_ERROR_NOSUCHFIELD, - '/does not allow null values/' - => DB_ERROR_CONSTRAINT_NOT_NULL, - '/Command has been aborted/' - => DB_ERROR_CONSTRAINT, - ); - } - - foreach ($error_regexps as $regexp => $code) { - if (preg_match($regexp, $errormsg)) { - return $code; - } - } - return DB_ERROR; - } - - // }}} - // {{{ sybaseRaiseError() - - /** - * Gather information about an error, then use that info to create a - * DB error object and finally return that object. - * - * @param integer $errno PEAR error number (usually a DB constant) if - * manually raising an error - * @return object DB error object - * @see errorNative() - * @see errorCode() - * @see DB_common::raiseError() - */ - function sybaseRaiseError($errno = null) - { - $native = $this->errorNative(); - if ($errno === null) { - $errno = $this->errorCode($native); - } - return $this->raiseError($errno, null, null, null, $native); - } - - // }}} // {{{ simpleQuery() /** - * Send a query to Sybase and return the results as a Sybase resource - * identifier. + * Sends a query to the database server * - * @param the SQL query + * @param string the SQL query string * - * @access public - * - * @return mixed returns a valid Sybase result for successful SELECT - * queries, DB_OK for other successful queries. A DB error is - * returned on failure. + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure */ function simpleQuery($query) { - $ismanip = DB::isManip($query); + $ismanip = $this->_checkManip($query); $this->last_query = $query; - if (!@sybase_select_db($this->_db, $this->connection)) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $query = $this->modifyQuery($query); @@ -264,11 +268,6 @@ class DB_sybase extends DB_common return $this->sybaseRaiseError(); } if (is_resource($result)) { - $numrows = $this->numRows($result); - if (is_object($numrows)) { - return $numrows; - } - $this->num_rows[(int)$result] = $numrows; return $result; } // Determine which queries that should return data, and which @@ -297,24 +296,26 @@ class DB_sybase extends DB_common // {{{ fetchInto() /** - * Fetch a row and insert the data into an existing array. + * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * - * @param resource $result query result identifier - * @param array $arr (reference) array where data from the row - * should be placed + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed - * @param int $rownum the row number to fetch + * @param int $rownum the row number to fetch (0 = first row) * - * @return mixed DB_OK on success, null when end of result set is - * reached or on failure + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure * * @see DB_result::fetchInto() - * @access private */ - function fetchInto($result, &$arr, $fetchmode, $rownum=null) + function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@sybase_data_seek($result, $rownum)) { @@ -340,13 +341,7 @@ class DB_sybase extends DB_common $arr = @sybase_fetch_row($result); } if (!$arr) { - // reported not work as seems that sybase_get_last_message() - // always return a message here - //if ($errmsg = @sybase_get_last_message()) { - // return $this->sybaseRaiseError($errmsg); - //} else { - return null; - //} + return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); @@ -361,31 +356,38 @@ class DB_sybase extends DB_common // {{{ freeResult() /** - * Free the internal resources associated with $result. + * Deletes the result set and frees the memory occupied by the result set * - * @param $result Sybase result identifier + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return bool true on success, false if $result is invalid + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() */ function freeResult($result) { - unset($this->num_rows[(int)$result]); - return @sybase_free_result($result); + return is_resource($result) ? sybase_free_result($result) : false; } // }}} // {{{ numCols() /** - * Get the number of columns in a result set. + * Gets the number of columns in a result set * - * @param $result Sybase result identifier + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return int the number of columns per row in $result + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() */ function numCols($result) { @@ -400,13 +402,17 @@ class DB_sybase extends DB_common // {{{ numRows() /** - * Get the number of rows in a result set. + * Gets the number of rows in a result set * - * @param $result Sybase result identifier + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. * - * @access public + * @param resource $result PHP's query result resource * - * @return int the number of rows in $result + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() */ function numRows($result) { @@ -421,14 +427,15 @@ class DB_sybase extends DB_common // {{{ affectedRows() /** - * Gets the number of rows affected by the data manipulation - * query. For other queries, this function returns 0. + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. * - * @return number of rows affected by the last query + * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { - if (DB::isManip($this->last_query)) { + if ($this->_last_query_manip) { $result = @sybase_affected_rows($this->connection); } else { $result = 0; @@ -444,18 +451,18 @@ class DB_sybase extends DB_common * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically - * created if it does not exist + * created if it does not exist * - * @return int the next id number in the sequence. DB_Error if problem. + * @return int the next id number in the sequence. + * A DB_Error object on failure. * - * @internal - * @see DB_common::nextID() - * @access public + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_sybase::createSequence(), DB_sybase::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); - if (!@sybase_select_db($this->_db, $this->connection)) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $repeat = 0; @@ -472,7 +479,7 @@ class DB_sybase extends DB_common return $this->raiseError($result); } } elseif (!DB::isError($result)) { - $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); + $result = $this->query("SELECT @@IDENTITY FROM $seqname"); $repeat = 0; } else { $repeat = false; @@ -490,19 +497,17 @@ class DB_sybase extends DB_common * * @param string $seq_name name of the new sequence * - * @return int DB_OK on success. A DB_Error object is returned if - * problems arise. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::createSequence() - * @access public + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_sybase::nextID(), DB_sybase::dropSequence() */ function createSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("CREATE TABLE $seqname ". - '(id numeric(10,0) IDENTITY NOT NULL ,' . - 'vapor int NULL)'); + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' (id numeric(10, 0) IDENTITY NOT NULL,' + . ' vapor int NULL)'); } // }}} @@ -513,43 +518,42 @@ class DB_sybase extends DB_common * * @param string $seq_name name of the sequence to be deleted * - * @return int DB_OK on success. DB_Error if problems. + * @return int DB_OK on success. A DB_Error object on failure. * - * @internal - * @see DB_common::dropSequence() - * @access public + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_sybase::nextID(), DB_sybase::createSequence() */ function dropSequence($seq_name) { - $seqname = $this->getSequenceName($seq_name); - return $this->query("DROP TABLE $seqname"); + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} - // {{{ getSpecialQuery() + // {{{ quoteFloat() /** - * Returns the query needed to get some backend info - * @param string $type What kind of info you want to retrieve - * @return string The SQL query string + * Formats a float value for use within a query in a locale-independent + * manner. + * + * @param float the float value to be quoted. + * @return string the quoted string. + * @see DB_common::quoteSmart() + * @since Method available since release 1.7.8. */ - function getSpecialQuery($type) - { - switch ($type) { - case 'tables': - return "select name from sysobjects where type = 'U' order by name"; - case 'views': - return "select name from sysobjects where type = 'V'"; - default: - return null; - } + function quoteFloat($float) { + return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); } - + // }}} // {{{ autoCommit() /** - * Enable/disable automatic commits + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { @@ -563,12 +567,14 @@ class DB_sybase extends DB_common // {{{ commit() /** - * Commit the current transaction. + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { - if (!@sybase_select_db($this->_db, $this->connection)) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $result = @sybase_query('COMMIT', $this->connection); @@ -584,12 +590,14 @@ class DB_sybase extends DB_common // {{{ rollback() /** - * Roll back (undo) the current transaction. + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { - if (!@sybase_select_db($this->_db, $this->connection)) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $result = @sybase_query('ROLLBACK', $this->connection); @@ -602,44 +610,146 @@ class DB_sybase extends DB_common } // }}} + // {{{ sybaseRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_sybase::errorNative(), DB_sybase::errorCode() + */ + function sybaseRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @sybase_get_last_message(); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + + // PHP 5.2+ prepends the function name to $php_errormsg, so we need + // this hack to work around it, per bug #9599. + $errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg); + + if (!isset($error_regexps)) { + $error_regexps = array( + '/Incorrect syntax near/' + => DB_ERROR_SYNTAX, + '/^Unclosed quote before the character string [\"\'].*[\"\']\./' + => DB_ERROR_SYNTAX, + '/Implicit conversion (from datatype|of NUMERIC value)/i' + => DB_ERROR_INVALID_NUMBER, + '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' + => DB_ERROR_NOSUCHTABLE, + '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' + => DB_ERROR_ACCESS_VIOLATION, + '/^.+ permission denied on object .+, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/^.* permission denied, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/[^.*] not found\./' + => DB_ERROR_NOSUCHTABLE, + '/There is already an object named/' + => DB_ERROR_ALREADY_EXISTS, + '/Invalid column name/' + => DB_ERROR_NOSUCHFIELD, + '/does not allow null values/' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/Command has been aborted/' + => DB_ERROR_CONSTRAINT, + '/^Cannot drop the index .* because it doesn\'t exist/i' + => DB_ERROR_NOT_FOUND, + '/^There is already an index/i' + => DB_ERROR_ALREADY_EXISTS, + '/^There are fewer columns in the INSERT statement than values specified/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + '/Divide by zero/i' + => DB_ERROR_DIVZERO, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} // {{{ tableInfo() /** - * Returns information about a table or a result set. + * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a - * string containing the name of a table + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. * @param int $mode a valid tableInfo mode - * @return array an associative array with the information requested - * or an error object if something is wrong - * @access public - * @internal - * @since 1.6.0 + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * * @see DB_common::tableInfo() + * @since Method available since Release 1.6.0 */ function tableInfo($result, $mode = null) { - if (isset($result->result)) { - /* - * Probably received a result object. - * Extract the result resource identifier. - */ - $id = $result->result; - $got_string = false; - } elseif (is_string($result)) { + if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ - if (!@sybase_select_db($this->_db, $this->connection)) { + if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $id = @sybase_query("SELECT * FROM $result WHERE 1=0", $this->connection); $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; } else { /* * Probably received a result resource identifier. @@ -661,60 +771,33 @@ class DB_sybase extends DB_common } $count = @sybase_num_fields($id); + $res = array(); - // made this IF due to performance (one if is faster than $count if's) - if (!$mode) { - - for ($i=0; $i<$count; $i++) { - $f = @sybase_fetch_field($id, $i); - - // column_source is often blank - if ($got_string) { - $res[$i]['table'] = $case_func($result); - } else { - $res[$i]['table'] = $case_func($f->column_source); - } - $res[$i]['name'] = $case_func($f->name); - $res[$i]['type'] = $f->type; - $res[$i]['len'] = $f->max_length; - if ($res[$i]['table']) { - $res[$i]['flags'] = $this->_sybase_field_flags( - $res[$i]['table'], $res[$i]['name']); - } else { - $res[$i]['flags'] = ''; - } - } - - } else { - // get full info - + if ($mode) { $res['num_fields'] = $count; + } - for ($i=0; $i<$count; $i++) { - $f = @sybase_fetch_field($id, $i); - - // column_source is often blank - if ($got_string) { - $res[$i]['table'] = $case_func($result); - } else { - $res[$i]['table'] = $case_func($f->column_source); - } - $res[$i]['name'] = $case_func($f->name); - $res[$i]['type'] = $f->type; - $res[$i]['len'] = $f->max_length; - if ($res[$i]['table']) { - $res[$i]['flags'] = $this->_sybase_field_flags( - $res[$i]['table'], $res[$i]['name']); - } else { - $res[$i]['flags'] = ''; - } - - if ($mode & DB_TABLEINFO_ORDER) { - $res['order'][$res[$i]['name']] = $i; - } - if ($mode & DB_TABLEINFO_ORDERTABLE) { - $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; - } + for ($i = 0; $i < $count; $i++) { + $f = @sybase_fetch_field($id, $i); + // column_source is often blank + $res[$i] = array( + 'table' => $got_string + ? $case_func($result) + : $case_func($f->column_source), + 'name' => $case_func($f->name), + 'type' => $f->type, + 'len' => $f->max_length, + 'flags' => '', + ); + if ($res[$i]['table']) { + $res[$i]['flags'] = $this->_sybase_field_flags( + $res[$i]['table'], $res[$i]['name']); + } + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } @@ -729,15 +812,17 @@ class DB_sybase extends DB_common // {{{ _sybase_field_flags() /** - * Get the flags for a field. + * Get the flags for a field * * Currently supports: * + unique_key (unique index, unique check or primary_key) * + multiple_key (multi-key index) * - * @param string $table table name - * @param string $column field name + * @param string $table the table name + * @param string $column the field name + * * @return string space delimited string of flags. Empty string if none. + * * @access private */ function _sybase_field_flags($table, $column) @@ -749,14 +834,24 @@ class DB_sybase extends DB_common $flags = array(); $tableName = $table; - // get unique/primary keys - $res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC); + /* We're running sp_helpindex directly because it doesn't exist in + * older versions of ASE -- unfortunately, we can't just use + * DB::isError() because the user may be using callback error + * handling. */ + $res = @sybase_query("sp_helpindex $table", $this->connection); - if (!isset($res[0]['index_description'])) { + if ($res === false || $res === true) { + // Fake a valid response for BC reasons. return ''; } - foreach ($res as $val) { + while (($val = sybase_fetch_assoc($res)) !== false) { + if (!isset($val['index_keys'])) { + /* No useful information returned. Break and be done with + * it, which preserves the pre-1.7.9 behaviour. */ + break; + } + $keys = explode(', ', trim($val['index_keys'])); if (sizeof($keys) > 1) { @@ -772,6 +867,8 @@ class DB_sybase extends DB_common } } + sybase_free_result($res); + } if (array_key_exists($column, $flags)) { @@ -786,10 +883,13 @@ class DB_sybase extends DB_common /** * Adds a string to the flags array if the flag is not yet in there - * - if there is no flag present the array is created. + * - if there is no flag present the array is created * * @param array $array reference of flags array to add a value to * @param mixed $value value to add to the flag array + * + * @return void + * * @access private */ function _add_flag(&$array, $value) @@ -802,23 +902,30 @@ class DB_sybase extends DB_common } // }}} - // {{{ quoteIdentifier() + // {{{ getSpecialQuery() /** - * Quote a string so it can be safely used as a table / column name + * Obtains the query string needed for listing a given type of objects * - * Quoting style depends on which database driver is being used. + * @param string $type the kind of objects you want to retrieve * - * @param string $str identifier name to be quoted + * @return string the SQL query string or null if the driver doesn't + * support the object type requested * - * @return string quoted identifier string - * - * @since 1.6.0 - * @access public + * @access protected + * @see DB_common::getListOf() */ - function quoteIdentifier($str) + function getSpecialQuery($type) { - return '[' . str_replace(']', ']]', $str) . ']'; + switch ($type) { + case 'tables': + return "SELECT name FROM sysobjects WHERE type = 'U'" + . ' ORDER BY name'; + case 'views': + return "SELECT name FROM sysobjects WHERE type = 'V'"; + default: + return null; + } } // }}} diff --git a/thirdparty/pear/File/Archive.php b/thirdparty/pear/File/Archive.php old mode 100644 new mode 100755 index 4b6254a..4b6254a --- a/thirdparty/pear/File/Archive.php +++ b/thirdparty/pear/File/Archive.php diff --git a/thirdparty/pear/File/Archive/Predicate.php b/thirdparty/pear/File/Archive/Predicate.php old mode 100644 new mode 100755 index 3685049..3685049 --- a/thirdparty/pear/File/Archive/Predicate.php +++ b/thirdparty/pear/File/Archive/Predicate.php diff --git a/thirdparty/pear/File/Archive/Predicate/And.php b/thirdparty/pear/File/Archive/Predicate/And.php old mode 100644 new mode 100755 index 97d15b9..97d15b9 --- a/thirdparty/pear/File/Archive/Predicate/And.php +++ b/thirdparty/pear/File/Archive/Predicate/And.php diff --git a/thirdparty/pear/File/Archive/Predicate/Current.php b/thirdparty/pear/File/Archive/Predicate/Current.php old mode 100644 new mode 100755 index e82db13..e82db13 --- a/thirdparty/pear/File/Archive/Predicate/Current.php +++ b/thirdparty/pear/File/Archive/Predicate/Current.php diff --git a/thirdparty/pear/File/Archive/Predicate/Custom.php b/thirdparty/pear/File/Archive/Predicate/Custom.php old mode 100644 new mode 100755 index 09492a3..09492a3 --- a/thirdparty/pear/File/Archive/Predicate/Custom.php +++ b/thirdparty/pear/File/Archive/Predicate/Custom.php diff --git a/thirdparty/pear/File/Archive/Predicate/Duplicate.php b/thirdparty/pear/File/Archive/Predicate/Duplicate.php old mode 100644 new mode 100755 index 2ca3066..2ca3066 --- a/thirdparty/pear/File/Archive/Predicate/Duplicate.php +++ b/thirdparty/pear/File/Archive/Predicate/Duplicate.php diff --git a/thirdparty/pear/File/Archive/Predicate/Ereg.php b/thirdparty/pear/File/Archive/Predicate/Ereg.php old mode 100644 new mode 100755 index 440142d..440142d --- a/thirdparty/pear/File/Archive/Predicate/Ereg.php +++ b/thirdparty/pear/File/Archive/Predicate/Ereg.php diff --git a/thirdparty/pear/File/Archive/Predicate/Eregi.php b/thirdparty/pear/File/Archive/Predicate/Eregi.php old mode 100644 new mode 100755 index 500d6bc..500d6bc --- a/thirdparty/pear/File/Archive/Predicate/Eregi.php +++ b/thirdparty/pear/File/Archive/Predicate/Eregi.php diff --git a/thirdparty/pear/File/Archive/Predicate/Extension.php b/thirdparty/pear/File/Archive/Predicate/Extension.php old mode 100644 new mode 100755 index 4770bd7..4770bd7 --- a/thirdparty/pear/File/Archive/Predicate/Extension.php +++ b/thirdparty/pear/File/Archive/Predicate/Extension.php diff --git a/thirdparty/pear/File/Archive/Predicate/False.php b/thirdparty/pear/File/Archive/Predicate/False.php old mode 100644 new mode 100755 index 3a9f34a..3a9f34a --- a/thirdparty/pear/File/Archive/Predicate/False.php +++ b/thirdparty/pear/File/Archive/Predicate/False.php diff --git a/thirdparty/pear/File/Archive/Predicate/Index.php b/thirdparty/pear/File/Archive/Predicate/Index.php old mode 100644 new mode 100755 index acbfb6f..acbfb6f --- a/thirdparty/pear/File/Archive/Predicate/Index.php +++ b/thirdparty/pear/File/Archive/Predicate/Index.php diff --git a/thirdparty/pear/File/Archive/Predicate/MIME.php b/thirdparty/pear/File/Archive/Predicate/MIME.php old mode 100644 new mode 100755 index 1da5123..1da5123 --- a/thirdparty/pear/File/Archive/Predicate/MIME.php +++ b/thirdparty/pear/File/Archive/Predicate/MIME.php diff --git a/thirdparty/pear/File/Archive/Predicate/MaxDepth.php b/thirdparty/pear/File/Archive/Predicate/MaxDepth.php old mode 100644 new mode 100755 index 01fb1de..01fb1de --- a/thirdparty/pear/File/Archive/Predicate/MaxDepth.php +++ b/thirdparty/pear/File/Archive/Predicate/MaxDepth.php diff --git a/thirdparty/pear/File/Archive/Predicate/MinSize.php b/thirdparty/pear/File/Archive/Predicate/MinSize.php old mode 100644 new mode 100755 index b52d45e..b52d45e --- a/thirdparty/pear/File/Archive/Predicate/MinSize.php +++ b/thirdparty/pear/File/Archive/Predicate/MinSize.php diff --git a/thirdparty/pear/File/Archive/Predicate/MinTime.php b/thirdparty/pear/File/Archive/Predicate/MinTime.php old mode 100644 new mode 100755 index a060288..a060288 --- a/thirdparty/pear/File/Archive/Predicate/MinTime.php +++ b/thirdparty/pear/File/Archive/Predicate/MinTime.php diff --git a/thirdparty/pear/File/Archive/Predicate/Not.php b/thirdparty/pear/File/Archive/Predicate/Not.php old mode 100644 new mode 100755 index 292d78e..292d78e --- a/thirdparty/pear/File/Archive/Predicate/Not.php +++ b/thirdparty/pear/File/Archive/Predicate/Not.php diff --git a/thirdparty/pear/File/Archive/Predicate/Or.php b/thirdparty/pear/File/Archive/Predicate/Or.php old mode 100644 new mode 100755 index 0e4388a..0e4388a --- a/thirdparty/pear/File/Archive/Predicate/Or.php +++ b/thirdparty/pear/File/Archive/Predicate/Or.php diff --git a/thirdparty/pear/File/Archive/Predicate/True.php b/thirdparty/pear/File/Archive/Predicate/True.php old mode 100644 new mode 100755 index 29efaba..29efaba --- a/thirdparty/pear/File/Archive/Predicate/True.php +++ b/thirdparty/pear/File/Archive/Predicate/True.php diff --git a/thirdparty/pear/File/Archive/Reader.php b/thirdparty/pear/File/Archive/Reader.php old mode 100644 new mode 100755 index 0149965..0149965 --- a/thirdparty/pear/File/Archive/Reader.php +++ b/thirdparty/pear/File/Archive/Reader.php diff --git a/thirdparty/pear/File/Archive/Reader/Ar.php b/thirdparty/pear/File/Archive/Reader/Ar.php old mode 100644 new mode 100755 index 60cb557..60cb557 --- a/thirdparty/pear/File/Archive/Reader/Ar.php +++ b/thirdparty/pear/File/Archive/Reader/Ar.php diff --git a/thirdparty/pear/File/Archive/Reader/Archive.php b/thirdparty/pear/File/Archive/Reader/Archive.php old mode 100644 new mode 100755 index 8f46218..8f46218 --- a/thirdparty/pear/File/Archive/Reader/Archive.php +++ b/thirdparty/pear/File/Archive/Reader/Archive.php diff --git a/thirdparty/pear/File/Archive/Reader/Bzip2.php b/thirdparty/pear/File/Archive/Reader/Bzip2.php old mode 100644 new mode 100755 index 20deb24..20deb24 --- a/thirdparty/pear/File/Archive/Reader/Bzip2.php +++ b/thirdparty/pear/File/Archive/Reader/Bzip2.php diff --git a/thirdparty/pear/File/Archive/Reader/Cache.php b/thirdparty/pear/File/Archive/Reader/Cache.php old mode 100644 new mode 100755 index 65097cd..65097cd --- a/thirdparty/pear/File/Archive/Reader/Cache.php +++ b/thirdparty/pear/File/Archive/Reader/Cache.php diff --git a/thirdparty/pear/File/Archive/Reader/ChangeName.php b/thirdparty/pear/File/Archive/Reader/ChangeName.php old mode 100644 new mode 100755 index 678b14d..678b14d --- a/thirdparty/pear/File/Archive/Reader/ChangeName.php +++ b/thirdparty/pear/File/Archive/Reader/ChangeName.php diff --git a/thirdparty/pear/File/Archive/Reader/Concat.php b/thirdparty/pear/File/Archive/Reader/Concat.php old mode 100644 new mode 100755 index ac57348..ac57348 --- a/thirdparty/pear/File/Archive/Reader/Concat.php +++ b/thirdparty/pear/File/Archive/Reader/Concat.php diff --git a/thirdparty/pear/File/Archive/Reader/Directory.php b/thirdparty/pear/File/Archive/Reader/Directory.php old mode 100644 new mode 100755 index 82832ab..82832ab --- a/thirdparty/pear/File/Archive/Reader/Directory.php +++ b/thirdparty/pear/File/Archive/Reader/Directory.php diff --git a/thirdparty/pear/File/Archive/Reader/File.php b/thirdparty/pear/File/Archive/Reader/File.php old mode 100644 new mode 100755 index e01f60e..e01f60e --- a/thirdparty/pear/File/Archive/Reader/File.php +++ b/thirdparty/pear/File/Archive/Reader/File.php diff --git a/thirdparty/pear/File/Archive/Reader/Filter.php b/thirdparty/pear/File/Archive/Reader/Filter.php old mode 100644 new mode 100755 index d17c459..d17c459 --- a/thirdparty/pear/File/Archive/Reader/Filter.php +++ b/thirdparty/pear/File/Archive/Reader/Filter.php diff --git a/thirdparty/pear/File/Archive/Reader/Gzip.php b/thirdparty/pear/File/Archive/Reader/Gzip.php old mode 100644 new mode 100755 index 4a6e099..4a6e099 --- a/thirdparty/pear/File/Archive/Reader/Gzip.php +++ b/thirdparty/pear/File/Archive/Reader/Gzip.php diff --git a/thirdparty/pear/File/Archive/Reader/Memory.php b/thirdparty/pear/File/Archive/Reader/Memory.php old mode 100644 new mode 100755 index 8b711b5..8b711b5 --- a/thirdparty/pear/File/Archive/Reader/Memory.php +++ b/thirdparty/pear/File/Archive/Reader/Memory.php diff --git a/thirdparty/pear/File/Archive/Reader/MimeList.php b/thirdparty/pear/File/Archive/Reader/MimeList.php old mode 100644 new mode 100755 index a7485f7..a7485f7 --- a/thirdparty/pear/File/Archive/Reader/MimeList.php +++ b/thirdparty/pear/File/Archive/Reader/MimeList.php diff --git a/thirdparty/pear/File/Archive/Reader/Multi.php b/thirdparty/pear/File/Archive/Reader/Multi.php old mode 100644 new mode 100755 index 6109ef1..6109ef1 --- a/thirdparty/pear/File/Archive/Reader/Multi.php +++ b/thirdparty/pear/File/Archive/Reader/Multi.php diff --git a/thirdparty/pear/File/Archive/Reader/Relay.php b/thirdparty/pear/File/Archive/Reader/Relay.php old mode 100644 new mode 100755 index acb9e44..acb9e44 --- a/thirdparty/pear/File/Archive/Reader/Relay.php +++ b/thirdparty/pear/File/Archive/Reader/Relay.php diff --git a/thirdparty/pear/File/Archive/Reader/Select.php b/thirdparty/pear/File/Archive/Reader/Select.php old mode 100644 new mode 100755 index ffdfd94..ffdfd94 --- a/thirdparty/pear/File/Archive/Reader/Select.php +++ b/thirdparty/pear/File/Archive/Reader/Select.php diff --git a/thirdparty/pear/File/Archive/Reader/Tar.php b/thirdparty/pear/File/Archive/Reader/Tar.php old mode 100644 new mode 100755 index 9b5898c..9b5898c --- a/thirdparty/pear/File/Archive/Reader/Tar.php +++ b/thirdparty/pear/File/Archive/Reader/Tar.php diff --git a/thirdparty/pear/File/Archive/Reader/Uncompress.php b/thirdparty/pear/File/Archive/Reader/Uncompress.php old mode 100644 new mode 100755 index 649e1fc..649e1fc --- a/thirdparty/pear/File/Archive/Reader/Uncompress.php +++ b/thirdparty/pear/File/Archive/Reader/Uncompress.php diff --git a/thirdparty/pear/File/Archive/Reader/Zip.php b/thirdparty/pear/File/Archive/Reader/Zip.php old mode 100644 new mode 100755 index f6ad64d..f6ad64d --- a/thirdparty/pear/File/Archive/Reader/Zip.php +++ b/thirdparty/pear/File/Archive/Reader/Zip.php diff --git a/thirdparty/pear/File/Archive/Writer.php b/thirdparty/pear/File/Archive/Writer.php old mode 100644 new mode 100755 index 71f7337..71f7337 --- a/thirdparty/pear/File/Archive/Writer.php +++ b/thirdparty/pear/File/Archive/Writer.php diff --git a/thirdparty/pear/File/Archive/Writer/AddBaseName.php b/thirdparty/pear/File/Archive/Writer/AddBaseName.php old mode 100644 new mode 100755 index 9c7d905..9c7d905 --- a/thirdparty/pear/File/Archive/Writer/AddBaseName.php +++ b/thirdparty/pear/File/Archive/Writer/AddBaseName.php diff --git a/thirdparty/pear/File/Archive/Writer/Ar.php b/thirdparty/pear/File/Archive/Writer/Ar.php old mode 100644 new mode 100755 index afb5b3e..afb5b3e --- a/thirdparty/pear/File/Archive/Writer/Ar.php +++ b/thirdparty/pear/File/Archive/Writer/Ar.php diff --git a/thirdparty/pear/File/Archive/Writer/Archive.php b/thirdparty/pear/File/Archive/Writer/Archive.php old mode 100644 new mode 100755 index 76cf8d1..76cf8d1 --- a/thirdparty/pear/File/Archive/Writer/Archive.php +++ b/thirdparty/pear/File/Archive/Writer/Archive.php diff --git a/thirdparty/pear/File/Archive/Writer/Bzip2.php b/thirdparty/pear/File/Archive/Writer/Bzip2.php old mode 100644 new mode 100755 index 864876e..864876e --- a/thirdparty/pear/File/Archive/Writer/Bzip2.php +++ b/thirdparty/pear/File/Archive/Writer/Bzip2.php diff --git a/thirdparty/pear/File/Archive/Writer/Files.php b/thirdparty/pear/File/Archive/Writer/Files.php old mode 100644 new mode 100755 index b2ceaf5..b2ceaf5 --- a/thirdparty/pear/File/Archive/Writer/Files.php +++ b/thirdparty/pear/File/Archive/Writer/Files.php diff --git a/thirdparty/pear/File/Archive/Writer/Gzip.php b/thirdparty/pear/File/Archive/Writer/Gzip.php old mode 100644 new mode 100755 index 5b10ff9..5b10ff9 --- a/thirdparty/pear/File/Archive/Writer/Gzip.php +++ b/thirdparty/pear/File/Archive/Writer/Gzip.php diff --git a/thirdparty/pear/File/Archive/Writer/Mail.php b/thirdparty/pear/File/Archive/Writer/Mail.php old mode 100644 new mode 100755 index 9c56f05..9c56f05 --- a/thirdparty/pear/File/Archive/Writer/Mail.php +++ b/thirdparty/pear/File/Archive/Writer/Mail.php diff --git a/thirdparty/pear/File/Archive/Writer/Memory.php b/thirdparty/pear/File/Archive/Writer/Memory.php old mode 100644 new mode 100755 index 9a508d0..9a508d0 --- a/thirdparty/pear/File/Archive/Writer/Memory.php +++ b/thirdparty/pear/File/Archive/Writer/Memory.php diff --git a/thirdparty/pear/File/Archive/Writer/MemoryArchive.php b/thirdparty/pear/File/Archive/Writer/MemoryArchive.php old mode 100644 new mode 100755 index 791c6fa..791c6fa --- a/thirdparty/pear/File/Archive/Writer/MemoryArchive.php +++ b/thirdparty/pear/File/Archive/Writer/MemoryArchive.php diff --git a/thirdparty/pear/File/Archive/Writer/Multi.php b/thirdparty/pear/File/Archive/Writer/Multi.php old mode 100644 new mode 100755 index fb7680a..fb7680a --- a/thirdparty/pear/File/Archive/Writer/Multi.php +++ b/thirdparty/pear/File/Archive/Writer/Multi.php diff --git a/thirdparty/pear/File/Archive/Writer/Output.php b/thirdparty/pear/File/Archive/Writer/Output.php old mode 100644 new mode 100755 index 790af52..790af52 --- a/thirdparty/pear/File/Archive/Writer/Output.php +++ b/thirdparty/pear/File/Archive/Writer/Output.php diff --git a/thirdparty/pear/File/Archive/Writer/Tar.php b/thirdparty/pear/File/Archive/Writer/Tar.php old mode 100644 new mode 100755 index c21dee9..c21dee9 --- a/thirdparty/pear/File/Archive/Writer/Tar.php +++ b/thirdparty/pear/File/Archive/Writer/Tar.php diff --git a/thirdparty/pear/File/Archive/Writer/UniqueAppender.php b/thirdparty/pear/File/Archive/Writer/UniqueAppender.php old mode 100644 new mode 100755 index fa1c731..fa1c731 --- a/thirdparty/pear/File/Archive/Writer/UniqueAppender.php +++ b/thirdparty/pear/File/Archive/Writer/UniqueAppender.php diff --git a/thirdparty/pear/File/Archive/Writer/Zip.php b/thirdparty/pear/File/Archive/Writer/Zip.php old mode 100644 new mode 100755 index ab97659..ab97659 --- a/thirdparty/pear/File/Archive/Writer/Zip.php +++ b/thirdparty/pear/File/Archive/Writer/Zip.php diff --git a/thirdparty/pear/File/CSV.php b/thirdparty/pear/File/CSV.php new file mode 100644 index 0000000..c780eb0 --- /dev/null +++ b/thirdparty/pear/File/CSV.php @@ -0,0 +1,628 @@ + + * @author Helgi ormar + * @copyright 2004-2005 The Authors + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: CSV.php,v 1.41 2007/05/20 12:25:14 dufuz Exp $ + * @link http://pear.php.net/package/File + */ + +require_once 'PEAR.php'; +require_once 'File.php'; + +/** +* File class for handling CSV files (Comma Separated Values), a common format +* for exchanging data. +* +* TODO: +* - Usage example and Doc +* - Use getPointer() in discoverFormat +* - Add a line counter for being able to output better error reports +* - Store the last error in GLOBALS and add File_CSV::getLastError() +* +* Wish: +* - Other methods like readAll(), writeAll(), numFields(), numRows() +* - Try to detect if a CSV has header or not in discoverFormat() (not possible with CSV) +* +* Known Bugs: +* (they has been analyzed but for the moment the impact in the speed for +* properly handle this uncommon cases is too high and won't be supported) +* - A field which is composed only by a single quoted separator (ie -> ;";";) +* is not handled properly +* - When there is exactly one field minus than the expected number and there +* is a field with a separator inside, the parser will throw the "wrong count" error +* +* Info about CSV and links to other sources +* http://www.shaftek.org/publications/drafts/mime-csv/draft-shafranovich-mime-csv-00.html#appendix +* +* @author Tomas V.V.Cox +* @author Helgi ormar +* @package File +*/ +class File_CSV +{ + /** + * This raiseError method works in a different way. It will always return + * false (an error occurred) but it will call PEAR::raiseError() before + * it. If no default PEAR global handler is set, will trigger an error. + * + * @param string $error The error message + * @return bool always false + */ + function raiseError($error) + { + // If a default PEAR Error handler is not set trigger the error + // XXX Add a PEAR::isSetHandler() method? + if ($GLOBALS['_PEAR_default_error_mode'] == PEAR_ERROR_RETURN) { + PEAR::raiseError($error, null, PEAR_ERROR_TRIGGER, E_USER_WARNING); + } else { + PEAR::raiseError($error); + } + return false; + } + + /** + * Checks the configuration given by the user + * + * @access private + * @param string &$error The error will be written here if any + * @param array &$conf The configuration assoc array + * @return string error Returns a error message + */ + function _conf(&$error, &$conf) + { + // check conf + if (!is_array($conf)) { + return $error = 'Invalid configuration'; + } + + if (!isset($conf['fields']) || !(int)$conf['fields']) { + return $error = 'The number of fields must be numeric (the "fields" key)'; + } + + if (isset($conf['sep'])) { + if (strlen($conf['sep']) != 1) { + return $error = 'Separator can only be one char'; + } + } elseif ($conf['fields'] > 1) { + return $error = 'Missing separator (the "sep" key)'; + } + + if (isset($conf['quote'])) { + if (strlen($conf['quote']) != 1) { + return $error = 'The quote char must be one char (the "quote" key)'; + } + } else { + $conf['quote'] = null; + } + + if (!isset($conf['crlf'])) { + $conf['crlf'] = "\n"; + } + + if (!isset($conf['eol2unix'])) { + $conf['eol2unix'] = true; + } + } + + /** + * Return or create the file descriptor associated with a file + * + * @param string $file The name of the file + * @param array &$conf The configuration + * @param string $mode The open node (ex: FILE_MODE_READ or FILE_MODE_WRITE) + * @param boolean $reset if passed as true and resource for the file exists + * than the file pointer will be moved to the beginning + * + * @return mixed A file resource or false + */ + function getPointer($file, &$conf, $mode = FILE_MODE_READ, $reset = false) + { + static $resources = array(); + static $config; + if (isset($resources[$file][$mode])) { + $conf = $config; + if ($reset) { + fseek($resources[$file][$mode], 0); + } + return $resources[$file][$mode]; + } + File_CSV::_conf($error, $conf); + if ($error) { + return File_CSV::raiseError($error); + } + $config = $conf; + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $fp = File::_getFilePointer($file, $mode); + PEAR::popErrorHandling(); + if (PEAR::isError($fp)) { + return File_CSV::raiseError($fp); + } + $resources[$file][$mode] = $fp; + return $fp; + } + + /** + * Unquote data + * + * @param string $field The data to unquote + * @param string $quote The quote char + * @return string the unquoted data + */ + function unquote($field, $quote) + { + // Trim first the string. + $field = trim($field); + $quote = trim($quote); + + // Incase null fields (form: ;;) + if (!strlen($field)) { + return $field; + } + + // excel compat + if ($field[0] == '=' && $field[1] == '"') { + $field = str_replace('="', '"', $field); + } + + $field_len = strlen($field); + if ($quote && $field[0] == $quote && $field[$field_len - 1] == $quote) { + // Get rid of escaping quotes + $new = $prev = $c = ''; + for ($i = 0; $i < $field_len; ++$i) { + $prev = $c; + $c = $field[$i]; + // Deal with escaping quotes + if ($c == $quote && $prev == $quote) { + $c = ''; + } + + $new .= $c; + } + $field = substr($new, 1, -1); + } + + return $field; + } + + /** + * Reads a row of data as an array from a CSV file. It's able to + * read memo fields with multiline data. + * + * @param string $file The filename where to write the data + * @param array &$conf The configuration of the dest CSV + * + * @return mixed Array with the data read or false on error/no more data + */ + function readQuoted($file, &$conf) + { + if (!$fp = File_CSV::getPointer($file, $conf, FILE_MODE_READ)) { + return false; + } + + $buff = $old = $prev = $c = ''; + $ret = array(); + $i = 1; + $in_quote = false; + $quote = $conf['quote']; + $f = $conf['fields']; + $sep = $conf['sep']; + while (false !== $ch = fgetc($fp)) { + $old = $prev; + $prev = $c; + $c = $ch; + + // Common case + if ($c != $quote && $c != $sep && $c != "\n" && $c != "\r") { + $buff .= $c; + continue; + } + + // Start quote. + if ( + $in_quote === false && + $quote && $c == $quote && + ( + $prev == $sep || $prev == "\n" || $prev === null || + $prev == "\r" || $prev == '' || $prev == ' ' + || $prev == '=' //excel compat + ) + ) { + $in_quote = true; + // excel compat, removing the = part but only if we are in a quote + if ($prev == '=') { + $buff{strlen($buff) - 1} = ''; + } + } + + if ($in_quote) { + + // When does the quote end, make sure it's not double quoted + if ($c == $sep && $prev == $quote && $old != $quote) { + $in_quote = false; + } elseif ($c == $sep && $buff == $quote.$quote) { + // In case we are dealing with double quote but empty value + $in_quote = false; + } elseif ($c == "\n" || $c == "\r") { + $sub = ($prev == "\r") ? 2 : 1; + $buff_len = strlen($buff); + if ( + $buff_len >= $sub && + $buff[$buff_len - $sub] == $quote + ) { + $in_quote = false; + } + } + } + + if (!$in_quote && ($c == $sep || $c == "\n" || $c == "\r") && $prev != '') { + // More fields than expected + if ($c == $sep && (count($ret) + 1) == $f) { + // Seek the pointer into linebreak character. + while (true) { + $c = fgetc($fp); + if ($c == "\n" || $c == "\r" || $c == '') { + break; + } + } + + // Insert last field value. + $ret[] = File_CSV::unquote($buff, $quote); + return $ret; + } + + // Less fields than expected + if (($c == "\n" || $c == "\r") && $i != $f) { + // Insert last field value. + $ret[] = File_CSV::unquote($buff, $quote); + if (count($ret) == 1 && empty($ret[0])) { + return array(); + } + + // Pair the array elements to fields count. - inserting empty values + $ret_count = count($ret); + $sum = ($f - 1) - ($ret_count - 1); + $data = array_merge($ret, array_fill($ret_count, $sum, '')); + return $data; + } + + if ($prev == "\r") { + $buff = substr($buff, 0, -1); + } + + // Convert EOL character to Unix EOL (LF). + if ($conf['eol2unix']) { + $buff = preg_replace('/(\r\n|\r)$/', "\n", $buff); + } + + $ret[] = File_CSV::unquote($buff, $quote); + if (count($ret) == $f) { + return $ret; + } + $buff = ''; + ++$i; + continue; + } + $buff .= $c; + } + + /* If it's the end of the file and we still have something in buffer + * then we process it since files can have no CL/FR at the end + */ + $feof = feof($fp); + if ($feof && !in_array($buff, array("\r", "\n", "\r\n")) && strlen($buff) > 0) { + $ret[] = File_CSV::unquote($buff, $quote); + if (count($ret) == $f) { + return $ret; + } + } + + return !$feof ? $ret : false; + } + + /** + * Reads a "row" from a CSV file and return it as an array + * + * @param string $file The CSV file + * @param array &$conf The configuration of the dest CSV + * + * @return mixed Array or false + */ + function read($file, &$conf) + { + static $headers = array(); + if (!$fp = File_CSV::getPointer($file, $conf, FILE_MODE_READ)) { + return false; + } + + // The size is limited to 4K + if (!$line = fgets($fp, 4096)) { + return false; + } + + $fields = $conf['fields'] == 1 ? array($line) : explode($conf['sep'], $line); + + $nl = array("\n", "\r", "\r\n"); + if (in_array($fields[count($fields) - 1], $nl)) { + array_pop($fields); + } + + $field_count = count($fields); + $last =& $fields[$field_count - 1]; + $len = strlen($last); + if ( + $field_count != $conf['fields'] || + $conf['quote'] && + ( + $len !== 0 && $last[$len - 1] == "\n" + && + ( + ($last[0] == $conf['quote'] + && $last[strlen(rtrim($last)) - 1] != $conf['quote']) + || + // excel support + ($last[0] == '=' && $last[1] == $conf['quote']) + || + // if the row has spaces before the quote + preg_match('|^\s+'.preg_quote($conf['quote']) .'|Ums', $last, $match) + ) + ) + // XXX perhaps there is a separator inside a quoted field + //preg_match("|{$conf['quote']}.*{$conf['sep']}.*{$conf['quote']}|U", $line) + ) { + fseek($fp, -1 * strlen($line), SEEK_CUR); + return File_CSV::readQuoted($file, $conf); + } else { + foreach ($fields as $k => $v) { + $fields[$k] = File_CSV::unquote($v, $conf['quote']); + } + } + + if (isset($conf['header']) && empty($headers)) { + // read the first row and assign to $headers + $headers = $fields; + return $headers; + } + + if ($field_count != $conf['fields']) { + File_CSV::raiseError("Read wrong fields number count: '". $field_count . + "' expected ".$conf['fields']); + return true; + } + + if (!empty($headers)) { + $tmp = array(); + foreach ($fields as $k => $v) { + $tmp[$headers[$k]] = $v; + } + $fields = $tmp; + } + + return $fields; + } + + /** + * Internal use only, will be removed in the future + * + * @param string $str The string to debug + * @access private + */ + function _dbgBuff($str) + { + if (strpos($str, "\r") !== false) { + $str = str_replace("\r", "_r_", $str); + } + if (strpos($str, "\n") !== false) { + $str = str_replace("\n", "_n_", $str); + } + if (strpos($str, "\t") !== false) { + $str = str_replace("\t", "_t_", $str); + } + if ($str === null) { + $str = '_NULL_'; + } + if ($str === '') { + $str = 'Empty string'; + } + echo "buff: ($str)\n"; + } + + /** + * Writes a struc (array) in a file as CSV + * + * @param string $file The filename where to write the data + * @param array $fields Ordered array with the data + * @param array &$conf The configuration of the dest CSV + * + * @return bool True on success false otherwise + */ + function write($file, $fields, &$conf) + { + if (!$fp = File_CSV::getPointer($file, $conf, FILE_MODE_WRITE)) { + return false; + } + + $field_count = count($fields); + if ($field_count != $conf['fields']) { + File_CSV::raiseError("Wrong fields number count: '". $field_count . + "' expected ".$conf['fields']); + return true; + } + + $write = ''; + for ($i = 0; $i < $field_count; ++$i) { + // only quote if the field contains a sep + if (!is_numeric($fields[$i]) && $conf['quote'] + && isset($conf['sep']) && strpos($fields[$i], $conf['sep']) + ) { + $write .= $conf['quote'] . $fields[$i] . $conf['quote']; + } else { + $write .= $fields[$i]; + } + + $write .= ($i < ($field_count - 1)) ? $conf['sep']: $conf['crlf']; + } + + if (!fwrite($fp, $write, strlen($write))) { + return File_CSV::raiseError('Can not write to file'); + } + + return true; + } + + /** + * Discover the format of a CSV file (the number of fields, the separator + * and if it quote string fields) + * + * @param string the CSV file name + * @param array extra separators that should be checked for. + * @return mixed Assoc array or false + */ + function discoverFormat($file, $extraSeps = array()) + { + if (!$fp = @fopen($file, 'rb')) { + return File_CSV::raiseError("Could not open file: $file"); + } + + // Set auto detect line ending for Mac EOL support + $oldini = ini_get('auto_detect_line_endings'); + if ($oldini != '1') { + ini_set('auto_detect_line_endings', '1'); + } + + // Take the first 30 lines and store the number of ocurrences + // for each separator in each line + $lines = ''; + for ($i = 0; $i < 30 && $line = fgets($fp, 4096); $i++) { + $lines .= $line; + } + fclose($fp); + + if ($oldini != '1') { + ini_set('auto_detect_line_endings', $oldini); + } + + $seps = array("\t", ';', ':', ','); + $seps = array_merge($seps, $extraSeps); + $matches = array(); + $quotes = '"\''; + + $lines = str_replace('""', '', $lines); + while ($lines != ($newLines = preg_replace('|((["\'])[^"]*(\2))|', '\2_\2', $lines))){ + $lines = $newLines; + } + + $eol = strpos($lines, "\r") ? "\r" : "\n"; + $lines = explode($eol, $lines); + foreach ($lines as $line) { + $orgLine = $line; + foreach ($seps as $sep) { + $line = preg_replace("|^[^$quotes$sep]*$sep*([$quotes][^$quotes]*[$quotes])|sm", '_', $orgLine); + // Find all seps that are within qoutes + ///FIXME ... counts legitimit lines as bad ones + + // In case there's a whitespace infront the field + $regex = '|\s*?'; + // Match the first quote (optional), also optionally match = since it's excel stuff + $regex.= "(?:\=?[$quotes])"; + $regex.= '(.*'; + // Don't match a sep if we are inside a quote + // also don't accept the sep if it has a quote on the either side + ///FIXME has to be possible if we are inside a quote! (tests fail because of this) + $regex.= "(?:[^$quotes])$sep(?:[^$quotes])"; + $regex.= '.*)'; + // Close quote (if it's present) and the sep (optional, could be end of line) + $regex.= "(?:[$quotes](?:$sep?))|Ums"; + preg_match_all($regex, $line, $match); + // Finding all seps, within quotes or not + $sep_count = substr_count($line, $sep); + // Real count + $matches[$sep][] = $sep_count - count($match[0]); + } + } + + $final = array(); + // Group the results by amount of equal ocurrences + foreach ($matches as $sep => $res) { + $times = array(); + $times[0] = 0; + foreach ($res as $k => $num) { + if ($num > 0) { + $times[$num] = (isset($times[$num])) ? $times[$num] + 1 : 1; + } + } + arsort($times); + + // Use max fields count. + $fields[$sep] = max(array_flip($times)); + $amount[$sep] = $times[key($times)]; + } + + arsort($amount); + $sep = key($amount); + + $conf['fields'] = $fields[$sep] + 1; + $conf['sep'] = $sep; + + // Test if there are fields with quotes around in the first 30 lines + $quote = null; + + $string = implode('', $lines); + foreach (array('"', '\'') as $q) { + if (preg_match_all("|$sep(?:\s*?)(\=?[$q]).*([$q])$sep|Us", $string, $match)) { + if ($match[1][0] == $match[2][0]) { + $quote = $match[1][0]; + break; + } + } + + if ( + preg_match_all("|^(\=?[$q]).*([$q])$sep{0,1}|Ums", $string, $match) + || preg_match_all("|(\=?[$q]).*([$q])$sep\s$|Ums", $string, $match) + ) { + if ($match[1][0] == $match[2][0]) { + $quote = $match[1][0]; + break; + } + } + } + + $conf['quote'] = $quote; + return $conf; + } + + /** + * Front to call getPointer and moving the resource to the + * beginning of the file + * Reset it if you like. + * + * @param string $file The name of the file + * @param array &$conf The configuration + * @param string $mode The open node (ex: FILE_MODE_READ or FILE_MODE_WRITE) + * + * @return boolean true on success false on failure + */ + function resetPointer($file, &$conf, $mode) + { + if (!File_CSV::getPointer($file, $conf, $mode, true)) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/thirdparty/pear/File/Gettext.php b/thirdparty/pear/File/Gettext.php old mode 100644 new mode 100755 index 9fc39c2..69e551f --- a/thirdparty/pear/File/Gettext.php +++ b/thirdparty/pear/File/Gettext.php @@ -11,7 +11,7 @@ * @author Michael Wallner * @copyright 2004-2005 Michael Wallner * @license BSD, revised - * @version CVS: $Id$ + * @version CVS: $Id: Gettext.php,v 1.7 2005/11/08 18:57:03 mike Exp $ * @link http://pear.php.net/package/File_Gettext */ @@ -30,7 +30,7 @@ ini_set('track_errors', true); * ################################################################# * * @author Michael Wallner - * @version $Revision$ + * @version $Revision: 1.7 $ * @access public */ class File_Gettext diff --git a/thirdparty/pear/File/Gettext/MO.php b/thirdparty/pear/File/Gettext/MO.php old mode 100644 new mode 100755 index a6ce1dd..521b489 --- a/thirdparty/pear/File/Gettext/MO.php +++ b/thirdparty/pear/File/Gettext/MO.php @@ -11,7 +11,7 @@ * @author Michael Wallner * @copyright 2004-2005 Michael Wallner * @license BSD, revised - * @version CVS: $Id$ + * @version CVS: $Id: MO.php,v 1.8 2006/01/07 09:45:25 mike Exp $ * @link http://pear.php.net/package/File_Gettext */ @@ -26,7 +26,7 @@ require_once 'File/Gettext.php'; * GNU MO file reader and writer. * * @author Michael Wallner - * @version $Revision$ + * @version $Revision: 1.8 $ * @access public */ class File_Gettext_MO extends File_Gettext @@ -147,6 +147,8 @@ class File_Gettext_MO extends File_Gettext */ function load($file = null) { + $this->strings = array(); + if (!isset($file)) { $file = $this->file; } diff --git a/thirdparty/pear/File/Gettext/PO.php b/thirdparty/pear/File/Gettext/PO.php old mode 100644 new mode 100755 index 8aeedbb..88eb1c1 --- a/thirdparty/pear/File/Gettext/PO.php +++ b/thirdparty/pear/File/Gettext/PO.php @@ -3,7 +3,7 @@ /** * File::Gettext - * + * * PHP versions 4 and 5 * * @category FileFormats @@ -11,7 +11,7 @@ * @author Michael Wallner * @copyright 2004-2005 Michael Wallner * @license BSD, revised - * @version CVS: $Id$ + * @version CVS: $Id: PO.php,v 1.6 2006/01/07 09:45:25 mike Exp $ * @link http://pear.php.net/package/File_Gettext */ @@ -20,13 +20,13 @@ */ require_once 'File/Gettext.php'; -/** +/** * File_Gettext_PO * * GNU PO file reader and writer. - * + * * @author Michael Wallner - * @version $Revision$ + * @version $Revision: 1.6 $ * @access public */ class File_Gettext_PO extends File_Gettext @@ -52,82 +52,48 @@ class File_Gettext_PO extends File_Gettext */ function load($file = null) { + $this->strings = array(); + if (!isset($file)) { $file = $this->file; } - + // load file if (!$contents = @file($file)) { return parent::raiseError($php_errormsg . ' ' . $file); } - - $msgid = null; - $aMatches = array(); - - foreach ($contents as $line) { - /* - Replaced the regular expressions to get translations working on windows. - */ - if (preg_match('/^msgid(.*)$/', $line, $aMatches)) { - if ($msgid) { - $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr); - } - $msgid = trim($aMatches[1]); - $msgid = substr($msgid, 1, strlen($msgid) - 2); - $msgstr = ""; - $msgstr_started = false; - } - //#^msgstr "(.*)"$# - if (preg_match('/^msgstr(.*)$/', $line, $aMatches)) { - $msgstr = trim($aMatches[1]); - $msgstr = substr($msgstr, 1, strlen($msgstr) - 2); - $msgstr_started = true; - } - //#^"(.*)"$# - if (preg_match('/^"(.*)"$/', $line, $aMatches)) { - if ($msgstr_started) { - $tmp = trim($aMatches[1]); - $msgstr .= substr($tmp, 1, strlen($tmp) - 2); - } else { - $tmp = trim($aMatches[1]); - $msgid .= substr($tmp, 1, strlen($tmp) - 2); - } - } - /* Original code - if (preg_match('#^msgid "(.*)"$#', $line, $aMatches)) { - if ($msgid) { - $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr); - } - $msgid = $aMatches[1]; - $msgstr = ""; - $msgstr_started = false; - } - if (preg_match('#^msgstr "(.*)"$#', $line, $aMatches)) { - $msgstr = $aMatches[1]; - $msgstr_started = true; - } - if (preg_match('#^"(.*)"$#', $line, $aMatches)) { - if ($msgstr_started) { - $msgstr .= $aMatches[1]; - } else { - $msgid .= $aMatches[1]; - } - } - */ + $contents = implode('', $contents); + + // match all msgid/msgstr entries + $matched = preg_match_all( + '/(msgid\s+("([^"]|\\\\")*?"\s*)+)\s+' . + '(msgstr\s+("([^"]|\\\\")*?"\s*)+)/', + $contents, $matches + ); + unset($contents); + + if (!$matched) { + return parent::raiseError('No msgid/msgstr entries found'); } - if ($msgid) { + + // get all msgids and msgtrs + for ($i = 0; $i < $matched; $i++) { + $msgid = preg_replace( + '/\s*msgid\s*"(.*)"\s*/s', '\\1', $matches[1][$i]); + $msgstr= preg_replace( + '/\s*msgstr\s*"(.*)"\s*/s', '\\1', $matches[4][$i]); $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr); } - + // check for meta info if (isset($this->strings[''])) { $this->meta = parent::meta2array($this->strings['']); unset($this->strings['']); } - + return true; } - + /** * Save PO file * @@ -140,7 +106,7 @@ class File_Gettext_PO extends File_Gettext if (!isset($file)) { $file = $this->file; } - + // open PO file if (!is_resource($fh = @fopen($file, 'w'))) { return parent::raiseError($php_errormsg . ' ' . $file); @@ -150,7 +116,7 @@ class File_Gettext_PO extends File_Gettext @fclose($fh); return parent::raiseError($php_errmsg . ' ' . $file); } - + // write meta info if (count($this->meta)) { $meta = 'msgid ""' . "\nmsgstr " . '""' . "\n"; @@ -166,7 +132,7 @@ class File_Gettext_PO extends File_Gettext 'msgstr "' . parent::prepare($t, true) . '"' . "\n\n" ); } - + //done @flock($fh, LOCK_UN); @fclose($fh); diff --git a/thirdparty/pear/File/Util.php b/thirdparty/pear/File/Util.php new file mode 100644 index 0000000..a59eb90 --- /dev/null +++ b/thirdparty/pear/File/Util.php @@ -0,0 +1,482 @@ + + * @copyright 2004-2005 Michael Wallner + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: Util.php,v 1.25 2007/02/20 14:19:08 mike Exp $ + * @link http://pear.php.net/package/File + */ + +/**#@+ + * Sorting Constants + */ +define('FILE_SORT_NONE', 0); +define('FILE_SORT_REVERSE', 1); +define('FILE_SORT_NAME', 2); +define('FILE_SORT_SIZE', 4); +define('FILE_SORT_DATE', 8); +define('FILE_SORT_RANDOM', 16); +/**#@-*/ + +/**#@+ + * Listing Constants + */ +define('FILE_LIST_FILES', 1); +define('FILE_LIST_DIRS', 2); +define('FILE_LIST_DOTS', 4); +define('FILE_LIST_ALL', FILE_LIST_FILES | FILE_LIST_DIRS | FILE_LIST_DOTS); +/**#@-*/ + +/** + * @ignore + */ +define('FILE_WIN32', defined('OS_WINDOWS') ? OS_WINDOWS : !strncasecmp(PHP_OS, 'win', 3)); + +/** + * File_Util + * + * File and directory utility functions. + * + * @access public + * @static + */ +class File_Util +{ + /** + * Returns a string path built from the array $pathParts. Where a join + * occurs multiple separators are removed. Joins using the optional + * separator, defaulting to the PHP DIRECTORY_SEPARATOR constant. + * + * @static + * @access public + * @param array $parts Array containing the parts to be joined + * @param string $separator The directory seperator + */ + function buildPath($parts, $separator = DIRECTORY_SEPARATOR) + { + $qs = '/^'. preg_quote($separator, '/') .'+$/'; + for ($i = 0, $c = count($parts); $i < $c; $i++) { + if (!strlen($parts[$i]) || preg_match($qs, $parts[$i])) { + unset($parts[$i]); + } elseif (0 == $i) { + $parts[$i] = rtrim($parts[$i], $separator); + } elseif ($c - 1 == $i) { + $parts[$i] = ltrim($parts[$i], $separator); + } else { + $parts[$i] = trim($parts[$i], $separator); + } + } + return implode($separator, $parts); + } + + /** + * Returns a path without leading / or C:\. If this is not + * present the path is returned as is. + * + * @static + * @access public + * @param string $path The path to be processed + * @return string The processed path or the path as is + */ + function skipRoot($path) + { + if (File_Util::isAbsolute($path)) { + if (FILE_WIN32) { + return substr($path, $path{3} == '\\' ? 4 : 3); + } + return ltrim($path, '/'); + } + return $path; + } + + /** + * Returns the temp directory according to either the TMP, TMPDIR, or + * TEMP env variables. If these are not set it will also check for the + * existence of /tmp, %WINDIR%\temp + * + * @static + * @access public + * @return string The system tmp directory + */ + function tmpDir() + { + if (FILE_WIN32) { + if (isset($_ENV['TEMP'])) { + return $_ENV['TEMP']; + } + if (isset($_ENV['TMP'])) { + return $_ENV['TMP']; + } + if (isset($_ENV['windir'])) { + return $_ENV['windir'] . '\\temp'; + } + if (isset($_ENV['SystemRoot'])) { + return $_ENV['SystemRoot'] . '\\temp'; + } + if (isset($_SERVER['TEMP'])) { + return $_SERVER['TEMP']; + } + if (isset($_SERVER['TMP'])) { + return $_SERVER['TMP']; + } + if (isset($_SERVER['windir'])) { + return $_SERVER['windir'] . '\\temp'; + } + if (isset($_SERVER['SystemRoot'])) { + return $_SERVER['SystemRoot'] . '\\temp'; + } + return '\temp'; + } + if (isset($_ENV['TMPDIR'])) { + return $_ENV['TMPDIR']; + } + if (isset($_SERVER['TMPDIR'])) { + return $_SERVER['TMPDIR']; + } + return '/tmp'; + } + + /** + * Returns a temporary filename using tempnam() and File::tmpDir(). + * + * @static + * @access public + * @param string $dirname Optional directory name for the tmp file + * @return string Filename and path of the tmp file + */ + function tmpFile($dirname = null) + { + if (!isset($dirname)) { + $dirname = File_Util::tmpDir(); + } + return tempnam($dirname, 'temp.'); + } + + /** + * Returns boolean based on whether given path is absolute or not. + * + * @static + * @access public + * @param string $path Given path + * @return boolean True if the path is absolute, false if it is not + */ + function isAbsolute($path) + { + if (preg_match('/(?:\/|\\\)\.\.(?=\/|$)/', $path)) { + return false; + } + if (FILE_WIN32) { + return preg_match('/^[a-zA-Z]:(\\\|\/)/', $path); + } + return ($path{0} == '/') || ($path{0} == '~'); + } + + /** + * Checks for a file's existence, taking the current include path + * into consideration + * + * This method can be called statically + * (e.g., File_Util::isIncludable('config.php')) + * + * @param string $file + * @param string $sep the directory separator (optional) + * @return string the includable path + * @access public + * @static + */ + function isIncludable($file, $sep = DIRECTORY_SEPARATOR) + { + foreach ((array) explode(PATH_SEPARATOR, ini_get('include_path')) as $path) { + if (file_exists($path .= $sep . $file)) { + return $path; + } + } + if (file_exists($file)) { + return $file; + } + return NULL; + } + + /** + * Get path relative to another path + * + * @static + * @access public + * @return string + * @param string $path + * @param string $root + * @param string $separator + */ + function relativePath($path, $root, $separator = DIRECTORY_SEPARATOR) + { + $path = File_Util::realpath($path, $separator); + $root = File_Util::realpath($root, $separator); + $dirs = explode($separator, $path); + $comp = explode($separator, $root); + + if (FILE_WIN32) { + if (strcasecmp($dirs[0], $comp[0])) { + return $path; + } + unset($dirs[0], $comp[0]); + } + + foreach ($comp as $i => $part) { + if (isset($dirs[$i]) && $part == $dirs[$i]) { + unset($dirs[$i], $comp[$i]); + } else { + break; + } + } + + return str_repeat('..' . $separator, count($comp)) . implode($separator, $dirs); + } + + /** + * Get real path (works with non-existant paths) + * + * @static + * @access public + * @return string + * @param string $path + * @param string $separator + */ + function realPath($path, $separator = DIRECTORY_SEPARATOR) + { + if (!strlen($path)) { + return $separator; + } + + $drive = ''; + if (FILE_WIN32) { + $path = preg_replace('/[\\\\\/]/', $separator, $path); + if (preg_match('/([a-zA-Z]\:)(.*)/', $path, $matches)) { + $drive = $matches[1]; + $path = $matches[2]; + } else { + $cwd = getcwd(); + $drive = substr($cwd, 0, 2); + if ($path{0} !== $separator{0}) { + $path = substr($cwd, 3) . $separator . $path; + } + } + } elseif ($path{0} !== $separator) { + $path = getcwd() . $separator . $path; + } + + $dirStack = array(); + foreach (explode($separator, $path) as $dir) { + if (strlen($dir) && $dir !== '.') { + if ($dir == '..') { + array_pop($dirStack); + } else { + $dirStack[] = $dir; + } + } + } + + return $drive . $separator . implode($separator, $dirStack); + } + + /** + * Check whether path is in root path + * + * @static + * @access public + * @return bool + * @param string $path + * @param string $root + */ + function pathInRoot($path, $root) + { + static $realPaths = array(); + + if (!isset($realPaths[$root])) { + $realPaths[$root] = File_Util::realPath($root); + } + + return false !== strstr(File_Util::realPath($path), $realPaths[$root]); + } + + /** + * List Directory + * + * The final argument, $cb, is a callback that either evaluates to true or + * false and performs a filter operation, or it can also modify the + * directory/file names returned. To achieve the latter effect use as + * follows: + * + * + * name, "\n"; + * } + * ?> + * + * + * @static + * @access public + * @return array + * @param string $path + * @param int $list + * @param int $sort + * @param mixed $cb + */ + function listDir($path, $list = FILE_LIST_ALL, $sort = FILE_SORT_NONE, $cb = null) + { + if (!strlen($path) || !is_dir($path)) { + return null; + } + + $entries = array(); + for ($dir = dir($path); false !== $entry = $dir->read(); ) { + if ($list & FILE_LIST_DOTS || $entry{0} !== '.') { + $isRef = ($entry === '.' || $entry === '..'); + $isDir = $isRef || is_dir($path .'/'. $entry); + if ( ((!$isDir && $list & FILE_LIST_FILES) || + ($isDir && $list & FILE_LIST_DIRS)) && + (!is_callable($cb) || + call_user_func_array($cb, array(&$entry)))) { + $entries[] = (object) array( + 'name' => $entry, + 'size' => $isDir ? null : filesize($path .'/'. $entry), + 'date' => filemtime($path .'/'. $entry), + ); + } + } + } + $dir->close(); + + if ($sort) { + $entries = File_Util::sortFiles($entries, $sort); + } + + return $entries; + } + + /** + * Sort Files + * + * @static + * @access public + * @return array + * @param array $files + * @param int $sort + */ + function sortFiles($files, $sort) + { + if (!$files) { + return array(); + } + + if (!$sort) { + return $files; + } + + if ($sort === 1) { + return array_reverse($files); + } + + if ($sort & FILE_SORT_RANDOM) { + shuffle($files); + return $files; + } + + $names = array(); + $sizes = array(); + $dates = array(); + + if ($sort & FILE_SORT_NAME) { + $r = &$names; + } elseif ($sort & FILE_SORT_DATE) { + $r = &$dates; + } elseif ($sort & FILE_SORT_SIZE) { + $r = &$sizes; + } else { + asort($files, SORT_REGULAR); + return $files; + } + + $sortFlags = array( + FILE_SORT_NAME => SORT_STRING, + FILE_SORT_DATE => SORT_NUMERIC, + FILE_SORT_SIZE => SORT_NUMERIC, + ); + + foreach ($files as $file) { + $names[] = $file->name; + $sizes[] = $file->size; + $dates[] = $file->date; + } + + if ($sort & FILE_SORT_REVERSE) { + arsort($r, $sortFlags[$sort & ~1]); + } else { + asort($r, $sortFlags[$sort]); + } + + $result = array(); + foreach ($r as $i => $f) { + $result[] = $files[$i]; + } + + return $result; + } + + /** + * Switch File Extension + * + * @static + * @access public + * @return string|array + * @param string|array $filename + * @param string $to new file extension + * @param string $from change only files with this extension + * @param bool $reverse change only files not having $from extension + */ + function switchExt($filename, $to, $from = null, $reverse = false) + { + if (is_array($filename)) { + foreach ($filename as $key => $file) { + $filename[$key] = File_Util::switchExt($file, $to, $from); + } + return $filename; + } + + if ($len = strlen($from)) { + $ext = substr($filename, -$len - 1); + $cfn = FILE_WIN32 ? 'strcasecmp' : 'strcmp'; + if (!$reverse == $cfn($ext, '.'. $from)) { + return $filename; + } + return substr($filename, 0, -$len - 1) .'.'. $to; + } + + if ($pos = strpos($filename, '.')) { + return substr($filename, 0, $pos) .'.'. $to; + } + + return $filename .'.'. $to; + } +} + +?> diff --git a/thirdparty/pear/GraphViz.php b/thirdparty/pear/GraphViz.php old mode 100644 new mode 100755 index 5224018..5224018 --- a/thirdparty/pear/GraphViz.php +++ b/thirdparty/pear/GraphViz.php diff --git a/thirdparty/pear/HTTP.php b/thirdparty/pear/HTTP.php old mode 100644 new mode 100755 index 72d90b6..72d90b6 --- a/thirdparty/pear/HTTP.php +++ b/thirdparty/pear/HTTP.php diff --git a/thirdparty/pear/HTTP/Client.php b/thirdparty/pear/HTTP/Client.php old mode 100644 new mode 100755 index dc6bfac..dc6bfac --- a/thirdparty/pear/HTTP/Client.php +++ b/thirdparty/pear/HTTP/Client.php diff --git a/thirdparty/pear/HTTP/Client/CookieManager.php b/thirdparty/pear/HTTP/Client/CookieManager.php old mode 100644 new mode 100755 index 407bdca..407bdca --- a/thirdparty/pear/HTTP/Client/CookieManager.php +++ b/thirdparty/pear/HTTP/Client/CookieManager.php diff --git a/thirdparty/pear/HTTP/Download.php b/thirdparty/pear/HTTP/Download.php old mode 100644 new mode 100755 index 04e8c8d..04e8c8d --- a/thirdparty/pear/HTTP/Download.php +++ b/thirdparty/pear/HTTP/Download.php diff --git a/thirdparty/pear/HTTP/Download/Archive.php b/thirdparty/pear/HTTP/Download/Archive.php old mode 100644 new mode 100755 index c3745fe..c3745fe --- a/thirdparty/pear/HTTP/Download/Archive.php +++ b/thirdparty/pear/HTTP/Download/Archive.php diff --git a/thirdparty/pear/HTTP/Download/PgLOB.php b/thirdparty/pear/HTTP/Download/PgLOB.php old mode 100644 new mode 100755 index bb97bf3..bb97bf3 --- a/thirdparty/pear/HTTP/Download/PgLOB.php +++ b/thirdparty/pear/HTTP/Download/PgLOB.php diff --git a/thirdparty/pear/HTTP/Header.php b/thirdparty/pear/HTTP/Header.php old mode 100644 new mode 100755 index 7ba3419..7ba3419 --- a/thirdparty/pear/HTTP/Header.php +++ b/thirdparty/pear/HTTP/Header.php diff --git a/thirdparty/pear/HTTP/Header/Cache.php b/thirdparty/pear/HTTP/Header/Cache.php old mode 100644 new mode 100755 index 58fb44a..58fb44a --- a/thirdparty/pear/HTTP/Header/Cache.php +++ b/thirdparty/pear/HTTP/Header/Cache.php diff --git a/thirdparty/pear/HTTP/Request.php b/thirdparty/pear/HTTP/Request.php old mode 100644 new mode 100755 index 5d48b39..ce980fa --- a/thirdparty/pear/HTTP/Request.php +++ b/thirdparty/pear/HTTP/Request.php @@ -1,52 +1,65 @@ | -// +-----------------------------------------------------------------------+ -// -// $Id$ -// -// HTTP_Request Class -// -// Simple example, (Fetches yahoo.com and displays it): -// -// $a = &new HTTP_Request('http://www.yahoo.com/'); -// $a->sendRequest(); -// echo $a->getResponseBody(); -// +/** + * Class for performing HTTP requests + * + * PHP versions 4 and 5 + * + * LICENSE: + * + * Copyright (c) 2002-2007, Richard Heyes + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * o Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * o The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request + * @author Richard Heyes + * @author Alexey Borzov + * @copyright 2002-2007 Richard Heyes + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Request.php,v 1.63 2008/10/11 11:07:10 avb Exp $ + * @link http://pear.php.net/package/HTTP_Request/ + */ +/** + * PEAR and PEAR_Error classes (for error handling) + */ require_once 'PEAR.php'; +/** + * Socket class + */ require_once 'Net/Socket.php'; +/** + * URL handling class + */ require_once 'Net/URL.php'; +/**#@+ + * Constants for HTTP request methods + */ define('HTTP_REQUEST_METHOD_GET', 'GET', true); define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true); define('HTTP_REQUEST_METHOD_POST', 'POST', true); @@ -54,15 +67,65 @@ define('HTTP_REQUEST_METHOD_PUT', 'PUT', true); define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true); define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true); define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true); - +/**#@-*/ + +/**#@+ + * Constants for HTTP request error codes + */ +define('HTTP_REQUEST_ERROR_FILE', 1); +define('HTTP_REQUEST_ERROR_URL', 2); +define('HTTP_REQUEST_ERROR_PROXY', 4); +define('HTTP_REQUEST_ERROR_REDIRECTS', 8); +define('HTTP_REQUEST_ERROR_RESPONSE', 16); +define('HTTP_REQUEST_ERROR_GZIP_METHOD', 32); +define('HTTP_REQUEST_ERROR_GZIP_READ', 64); +define('HTTP_REQUEST_ERROR_GZIP_DATA', 128); +define('HTTP_REQUEST_ERROR_GZIP_CRC', 256); +/**#@-*/ + +/**#@+ + * Constants for HTTP protocol versions + */ define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true); define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true); +/**#@-*/ -class HTTP_Request { +if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) { + /** + * Whether string functions are overloaded by their mbstring equivalents + */ + define('HTTP_REQUEST_MBSTRING', true); +} else { + /** + * @ignore + */ + define('HTTP_REQUEST_MBSTRING', false); +} +/** + * Class for performing HTTP requests + * + * Simple example (fetches yahoo.com and displays it): + * + * $a = &new HTTP_Request('http://www.yahoo.com/'); + * $a->sendRequest(); + * echo $a->getResponseBody(); + * + * + * @category HTTP + * @package HTTP_Request + * @author Richard Heyes + * @author Alexey Borzov + * @version Release: 1.4.4 + */ +class HTTP_Request +{ + /**#@+ + * @access private + */ /** * Instance of Net_URL - * @var object Net_URL + * @var Net_URL */ var $_url; @@ -89,7 +152,7 @@ class HTTP_Request { * @var string */ var $_user; - + /** * Basic Auth Password * @var string @@ -98,28 +161,28 @@ class HTTP_Request { /** * Socket object - * @var object Net_Socket + * @var Net_Socket */ var $_sock; - + /** * Proxy server * @var string */ var $_proxy_host; - + /** * Proxy port * @var integer */ var $_proxy_port; - + /** * Proxy username * @var string */ var $_proxy_user; - + /** * Proxy password * @var string @@ -133,7 +196,7 @@ class HTTP_Request { var $_postData; /** - * Request body + * Request body * @var string */ var $_body; @@ -145,7 +208,17 @@ class HTTP_Request { var $_bodyDisallowed = array('TRACE'); /** - * Files to post + * Methods having defined semantics for request body + * + * Content-Length header (indicating that the body follows, section 4.3 of + * RFC 2616) will be sent for these methods even if no body was added + * + * @var array + */ + var $_bodyRequired = array('POST', 'PUT'); + + /** + * Files to post * @var array */ var $_postFiles = array(); @@ -155,25 +228,25 @@ class HTTP_Request { * @var float */ var $_timeout; - + /** * HTTP_Response object - * @var object HTTP_Response + * @var HTTP_Response */ var $_response; - + /** * Whether to allow redirects * @var boolean */ var $_allowRedirects; - + /** * Maximum redirects allowed * @var integer */ var $_maxRedirects; - + /** * Current number of redirects * @var integer @@ -193,7 +266,7 @@ class HTTP_Request { var $_listeners = array(); /** - * Whether to save response body in response object property + * Whether to save response body in response object property * @var bool */ var $_saveBody = true; @@ -209,6 +282,7 @@ class HTTP_Request { * @var array */ var $_socketOptions = null; + /**#@-*/ /** * Constructor @@ -237,7 +311,6 @@ class HTTP_Request { */ function HTTP_Request($url = '', $params = array()) { - $this->_sock = &new Net_Socket(); $this->_method = HTTP_REQUEST_METHOD_GET; $this->_http = HTTP_REQUEST_HTTP_VER_1_1; $this->_requestHeaders = array(); @@ -270,7 +343,7 @@ class HTTP_Request { // Default useragent $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )'); - // Make sure keepalives dont knobble us + // We don't do keep-alives by default $this->addHeader('Connection', 'close'); // Basic authentication @@ -278,15 +351,17 @@ class HTTP_Request { $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass)); } - // Use gzip encoding if possible - // Avoid gzip encoding if using multibyte functions (see #1781) - if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib') && - 0 == (2 & ini_get('mbstring.func_overload'))) { + // Proxy authentication (see bug #5913) + if (!empty($this->_proxy_user)) { + $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass)); + } + // Use gzip encoding if possible + if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) { $this->addHeader('Accept-Encoding', 'gzip'); } } - + /** * Generates a Host header for HTTP/1.1 requests * @@ -303,14 +378,14 @@ class HTTP_Request { } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) { $host = $this->_url->host . ':' . $this->_url->port; - + } else { $host = $this->_url->host; } return $host; } - + /** * Resets the object to its initial state (DEPRECATED). * Takes the same parameters as the constructor. @@ -343,8 +418,24 @@ class HTTP_Request { if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) { $this->addHeader('Host', $this->_generateHostHeader()); } + + // set '/' instead of empty path rather than check later (see bug #8662) + if (empty($this->_url->path)) { + $this->_url->path = '/'; + } + } + + /** + * Returns the current request URL + * + * @return string Current request URL + * @access public + */ + function getUrl() + { + return empty($this->_url)? '': $this->_url->getUrl(); } - + /** * Sets a proxy to be used * @@ -438,8 +529,8 @@ class HTTP_Request { function addQueryString($name, $value, $preencoded = false) { $this->_url->addQueryString($name, $value, $preencoded); - } - + } + /** * Sets the querystring to literally what you supply * @@ -471,7 +562,7 @@ class HTTP_Request { /** * Recursively applies the callback function to the value - * + * * @param mixed Callback function * @param mixed Value to process * @access private @@ -491,10 +582,14 @@ class HTTP_Request { } /** - * Adds a file to upload - * - * This also changes content-type to 'multipart/form-data' for proper upload - * + * Adds a file to form-based file upload + * + * Used to emulate file upload via a HTML form. The method also sets + * Content-Type of HTTP request to 'multipart/form-data'. + * + * If you just want to send the contents of a file as the body of HTTP + * request you should use setBody() method. + * * @access public * @param string name of file-upload field * @param mixed file name(s) @@ -505,11 +600,11 @@ class HTTP_Request { function addFile($inputName, $fileName, $contentType = 'application/octet-stream') { if (!is_array($fileName) && !is_readable($fileName)) { - return PEAR::raiseError("File '{$fileName}' is not readable"); + return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE); } elseif (is_array($fileName)) { foreach ($fileName as $name) { if (!is_readable($name)) { - return PEAR::raiseError("File '{$name}' is not readable"); + return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE); } } } @@ -527,7 +622,7 @@ class HTTP_Request { * @param string The data * @param bool Whether data is preencoded or not, default = already encoded * @access public - * @deprecated deprecated since 1.3.0, method addBody() should be used instead + * @deprecated deprecated since 1.3.0, method setBody() should be used instead */ function addRawPostData($postdata, $preencoded = true) { @@ -546,8 +641,8 @@ class HTTP_Request { } /** - * Clears any postdata that has been added (DEPRECATED). - * + * Clears any postdata that has been added (DEPRECATED). + * * Useful for multiple request scenarios. * * @access public @@ -560,7 +655,7 @@ class HTTP_Request { /** * Appends a cookie to "Cookie:" header - * + * * @param string $name cookie name * @param string $value cookie value * @access public @@ -570,10 +665,10 @@ class HTTP_Request { $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : ''; $this->addHeader('Cookie', $cookies . $name . '=' . $value); } - + /** - * Clears any cookies that have been added (DEPRECATED). - * + * Clears any cookies that have been added (DEPRECATED). + * * Useful for multiple request scenarios * * @access public @@ -595,17 +690,19 @@ class HTTP_Request { function sendRequest($saveBody = true) { if (!is_a($this->_url, 'Net_URL')) { - return PEAR::raiseError('No URL given.'); + return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL); } $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host; $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port; - // 4.3.0 supports SSL connections using OpenSSL. The function test determines - // we running on at least 4.3.0 - if (strcasecmp($this->_url->protocol, 'https') == 0 AND function_exists('file_get_contents') AND extension_loaded('openssl')) { - if (isset($this->_proxy_host)) { - return PEAR::raiseError('HTTPS proxies are not supported.'); + if (strcasecmp($this->_url->protocol, 'https') == 0) { + // Bug #14127, don't try connecting to HTTPS sites without OpenSSL + if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) { + return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests', + HTTP_REQUEST_ERROR_URL); + } elseif (isset($this->_proxy_host)) { + return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY); } $host = 'ssl://' . $host; } @@ -614,9 +711,31 @@ class HTTP_Request { $magicQuotes = ini_get('magic_quotes_runtime'); ini_set('magic_quotes_runtime', false); - // If this is a second request, we may get away without - // re-connecting if they're on the same server - $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions); + // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive + // connection token to a proxy server... + if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) && + 'Keep-Alive' == $this->_requestHeaders['connection']) + { + $this->removeHeader('connection'); + } + + $keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) || + (!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']); + $sockets = &PEAR::getStaticProperty('HTTP_Request', 'sockets'); + $sockKey = $host . ':' . $port; + unset($this->_sock); + + // There is a connected socket in the "static" property? + if ($keepAlive && !empty($sockets[$sockKey]) && + !empty($sockets[$sockKey]->fp)) + { + $this->_sock =& $sockets[$sockKey]; + $err = null; + } else { + $this->_notify('connect'); + $this->_sock =& new Net_Socket(); + $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions); + } PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest()); if (!PEAR::isError($err)) { @@ -628,7 +747,23 @@ class HTTP_Request { // Read the response $this->_response = &new HTTP_Response($this->_sock, $this->_listeners); - $err = $this->_response->process($this->_saveBody && $saveBody); + $err = $this->_response->process( + $this->_saveBody && $saveBody, + HTTP_REQUEST_METHOD_HEAD != $this->_method + ); + + if ($keepAlive) { + $keepAlive = (isset($this->_response->_headers['content-length']) + || (isset($this->_response->_headers['transfer-encoding']) + && strtolower($this->_response->_headers['transfer-encoding']) == 'chunked')); + if ($keepAlive) { + if (isset($this->_response->_headers['connection'])) { + $keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive'; + } else { + $keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol; + } + } + } } ini_set('magic_quotes_runtime', $magicQuotes); @@ -637,6 +772,12 @@ class HTTP_Request { return $err; } + if (!$keepAlive) { + $this->disconnect(); + // Store the connected socket in "static" property + } elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) { + $sockets[$sockKey] =& $this->_sock; + } // Check for redirection if ( $this->_allowRedirects @@ -645,7 +786,7 @@ class HTTP_Request { AND $this->getResponseCode() < 399 AND !empty($this->_response->_headers['location'])) { - + $redirect = $this->_response->_headers['location']; // Absolute URL @@ -655,7 +796,7 @@ class HTTP_Request { // Absolute path } elseif ($redirect{0} == '/') { $this->_url->path = $redirect; - + // Relative path } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') { if (substr($this->_url->path, -1) == '/') { @@ -665,7 +806,7 @@ class HTTP_Request { } $redirect = Net_URL::resolvePath($redirect); $this->_url->path = $redirect; - + // Filename, no path } else { if (substr($this->_url->path, -1) == '/') { @@ -681,15 +822,26 @@ class HTTP_Request { // Too many redirects } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) { - return PEAR::raiseError('Too many redirects'); + return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS); } - $this->_sock->disconnect(); - return true; } /** + * Disconnect the socket, if connected. Only useful if using Keep-Alive. + * + * @access public + */ + function disconnect() + { + if (!empty($this->_sock) && !empty($this->_sock->fp)) { + $this->_notify('disconnect'); + $this->_sock->disconnect(); + } + } + + /** * Returns the response code * * @access public @@ -701,6 +853,17 @@ class HTTP_Request { } /** + * Returns the response reason phrase + * + * @access public + * @return mixed Response reason phrase, false if not set + */ + function getResponseReason() + { + return isset($this->_response->_reason) ? $this->_response->_reason : false; + } + + /** * Returns either the named header or all if no name given * * @access public @@ -731,7 +894,7 @@ class HTTP_Request { /** * Returns cookies set in response - * + * * @access public * @return mixed array of response cookies, false if none are present */ @@ -755,15 +918,19 @@ class HTTP_Request { $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : ''; $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : ''; - $path = (empty($this->_url->path)? '/': $this->_url->path) . $querystring; + $path = $this->_url->path . $querystring; $url = $host . $port . $path; + if (!strlen($url)) { + $url = '/'; + } + $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n"; if (in_array($this->_method, $this->_bodyDisallowed) || - (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body)) || - (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_postData) && empty($this->_postFiles))) { - + (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method || + (empty($this->_postData) && empty($this->_postFiles))))) + { $this->removeHeader('Content-Type'); } else { if (empty($this->_requestHeaders['content-type'])) { @@ -783,20 +950,19 @@ class HTTP_Request { } } - // No post data or wrong method, so simply add a final CRLF - if (in_array($this->_method, $this->_bodyDisallowed) || - (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body))) { + // Method does not allow a body, simply add a final CRLF + if (in_array($this->_method, $this->_bodyDisallowed)) { $request .= "\r\n"; // Post data if it's an array - } elseif (HTTP_REQUEST_METHOD_POST == $this->_method && + } elseif (HTTP_REQUEST_METHOD_POST == $this->_method && (!empty($this->_postData) || !empty($this->_postFiles))) { // "normal" POST request if (!isset($boundary)) { $postdata = implode('&', array_map( - create_function('$a', 'return $a[0] . \'=\' . $a[1];'), + create_function('$a', 'return $a[0] . \'=\' . $a[1];'), $this->_flattenArray('', $this->_postData) )); @@ -819,30 +985,42 @@ class HTTP_Request { $value['name'] = array($value['name']); } foreach ($value['name'] as $key => $filename) { - $fp = fopen($filename, 'r'); - $data = fread($fp, filesize($filename)); - fclose($fp); + $fp = fopen($filename, 'r'); $basename = basename($filename); $type = is_array($value['type'])? @$value['type'][$key]: $value['type']; $postdata .= '--' . $boundary . "\r\n"; $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"'; $postdata .= "\r\nContent-Type: " . $type; - $postdata .= "\r\n\r\n" . $data . "\r\n"; + $postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n"; + fclose($fp); } } $postdata .= '--' . $boundary . "--\r\n"; } - $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n"; + $request .= 'Content-Length: ' . + (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) . + "\r\n\r\n"; $request .= $postdata; // Explicitly set request body - } elseif (!empty($this->_body)) { + } elseif (0 < strlen($this->_body)) { - $request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n"; + $request .= 'Content-Length: ' . + (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) . + "\r\n\r\n"; $request .= $this->_body; + + // No body: send a Content-Length header nonetheless (request #12900), + // but do that only for methods that require a body (bug #14740) + } else { + + if (in_array($this->_method, $this->_bodyRequired)) { + $request .= "Content-Length: 0\r\n"; + } + $request .= "\r\n"; } - + return $request; } @@ -853,6 +1031,7 @@ class HTTP_Request { * @param string name for item * @param mixed item's values * @return array array with the following items: array('item name', 'item value'); + * @access private */ function _flattenArray($name, $values) { @@ -878,9 +1057,20 @@ class HTTP_Request { /** * Adds a Listener to the list of listeners that are notified of * the object's events - * - * @param object HTTP_Request_Listener instance to attach - * @return boolean whether the listener was successfully attached + * + * Events sent by HTTP_Request object + * - 'connect': on connection to server + * - 'sentRequest': after the request was sent + * - 'disconnect': on disconnection from server + * + * Events sent by HTTP_Response object + * - 'gotHeaders': after receiving response headers (headers are passed in $data) + * - 'tick': on receiving a part of response body (the part is passed in $data) + * - 'gzTick': on receiving a gzip-encoded part of response body (ditto) + * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped) + * + * @param HTTP_Request_Listener listener to attach + * @return boolean whether the listener was successfully attached * @access public */ function attach(&$listener) @@ -894,15 +1084,15 @@ class HTTP_Request { /** - * Removes a Listener from the list of listeners - * - * @param object HTTP_Request_Listener instance to detach - * @return boolean whether the listener was successfully detached + * Removes a Listener from the list of listeners + * + * @param HTTP_Request_Listener listener to detach + * @return boolean whether the listener was successfully detached * @access public */ function detach(&$listener) { - if (!is_a($listener, 'HTTP_Request_Listener') || + if (!is_a($listener, 'HTTP_Request_Listener') || !isset($this->_listeners[$listener->getId()])) { return false; } @@ -913,18 +1103,11 @@ class HTTP_Request { /** * Notifies all registered listeners of an event. - * - * Events sent by HTTP_Request object - * 'sentRequest': after the request was sent - * Events sent by HTTP_Response object - * 'gotHeaders': after receiving response headers (headers are passed in $data) - * 'tick': on receiving a part of response body (the part is passed in $data) - * 'gzTick': on receiving a gzip-encoded part of response body (ditto) - * 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped) - * + * * @param string Event name * @param mixed Additional data * @access private + * @see HTTP_Request::attach() */ function _notify($event, $data = null) { @@ -936,13 +1119,19 @@ class HTTP_Request { /** -* Response class to complement the Request class -*/ + * Response class to complement the Request class + * + * @category HTTP + * @package HTTP_Request + * @author Richard Heyes + * @author Alexey Borzov + * @version Release: 1.4.4 + */ class HTTP_Response { /** * Socket object - * @var object + * @var Net_Socket */ var $_sock; @@ -951,13 +1140,19 @@ class HTTP_Response * @var string */ var $_protocol; - + /** * Return code * @var string */ var $_code; - + + /** + * Response reason phrase + * @var string + */ + var $_reason; + /** * Response headers * @var array @@ -965,7 +1160,7 @@ class HTTP_Response var $_headers; /** - * Cookies set in response + * Cookies set in response * @var array */ var $_cookies; @@ -988,12 +1183,17 @@ class HTTP_Response */ var $_listeners = array(); + /** + * Bytes left to read from message-body + * @var null|int + */ + var $_toRead; + /** * Constructor * - * @param object Net_Socket socket to read the response from - * @param array listeners attached to request - * @return mixed PEAR Error on error, true otherwise + * @param Net_Socket socket to read the response from + * @param array listeners attached to request */ function HTTP_Response(&$sock, &$listeners) { @@ -1004,26 +1204,29 @@ class HTTP_Response /** * Processes a HTTP response - * - * This extracts response code, headers, cookies and decodes body if it + * + * This extracts response code, headers, cookies and decodes body if it * was encoded in some way * * @access public * @param bool Whether to store response body in object property, set * this to false if downloading a LARGE file and using a Listener. * This is assumed to be true if body is gzip-encoded. + * @param bool Whether the response can actually have a message-body. + * Will be set to false for HEAD requests. * @throws PEAR_Error * @return mixed true on success, PEAR_Error in case of malformed response */ - function process($saveBody = true) + function process($saveBody = true, $canHaveBody = true) { do { $line = $this->_sock->readLine(); - if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) { - return PEAR::raiseError('Malformed response.'); + if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) { + return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE); } else { - $this->_protocol = 'HTTP/' . $http_version; - $this->_code = intval($returncode); + $this->_protocol = $s[1]; + $this->_code = intval($s[2]); + $this->_reason = empty($s[3])? null: $s[3]; } while ('' !== ($header = $this->_sock->readLine())) { $this->_processHeader($header); @@ -1032,18 +1235,37 @@ class HTTP_Response $this->_notify('gotHeaders', $this->_headers); + // RFC 2616, section 4.4: + // 1. Any response message which "MUST NOT" include a message-body ... + // is always terminated by the first empty line after the header fields + // 3. ... If a message is received with both a + // Transfer-Encoding header field and a Content-Length header field, + // the latter MUST be ignored. + $canHaveBody = $canHaveBody && $this->_code >= 200 && + $this->_code != 204 && $this->_code != 304; + // If response body is present, read it and decode $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']); $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']); $hasBody = false; - if (!isset($this->_headers['content-length']) || 0 != $this->_headers['content-length']) { - while (!$this->_sock->eof()) { + if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) || + 0 != $this->_headers['content-length'])) + { + if ($chunked || !isset($this->_headers['content-length'])) { + $this->_toRead = null; + } else { + $this->_toRead = $this->_headers['content-length']; + } + while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) { if ($chunked) { $data = $this->_readChunked(); - } else { + } elseif (is_null($this->_toRead)) { $data = $this->_sock->read(4096); + } else { + $data = $this->_sock->read(min(4096, $this->_toRead)); + $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data); } - if ('' == $data) { + if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) { break; } else { $hasBody = true; @@ -1054,10 +1276,15 @@ class HTTP_Response } } } + if ($hasBody) { // Uncompress the body if needed if ($gzipped) { - $this->_body = gzinflate(substr($this->_body, 10)); + $body = $this->_decodeGzip($this->_body); + if (PEAR::isError($body)) { + return $body; + } + $this->_body = $body; $this->_notify('gotBody', $this->_body); } else { $this->_notify('gotBody'); @@ -1075,10 +1302,13 @@ class HTTP_Response */ function _processHeader($header) { + if (false === strpos($header, ':')) { + return; + } list($headername, $headervalue) = explode(':', $header, 2); $headername = strtolower($headername); $headervalue = ltrim($headervalue); - + if ('set-cookie' != $headername) { if (isset($this->_headers[$headername])) { $this->_headers[$headername] .= ',' . $headervalue; @@ -1144,7 +1374,7 @@ class HTTP_Response /** * Read a part of response body encoded with chunked Transfer-Encoding - * + * * @access private * @return string */ @@ -1154,7 +1384,7 @@ class HTTP_Response if (0 == $this->_chunkLength) { $line = $this->_sock->readLine(); if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) { - $this->_chunkLength = hexdec($matches[1]); + $this->_chunkLength = hexdec($matches[1]); // Chunk with zero length indicates the end if (0 == $this->_chunkLength) { $this->_sock->readLine(); // make this an eof() @@ -1165,7 +1395,7 @@ class HTTP_Response } } $data = $this->_sock->read($this->_chunkLength); - $this->_chunkLength -= strlen($data); + $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data); if (0 == $this->_chunkLength) { $this->_sock->readLine(); // Trailing CRLF } @@ -1175,7 +1405,7 @@ class HTTP_Response /** * Notifies all registered listeners of an event. - * + * * @param string Event name * @param mixed Additional data * @access private @@ -1187,5 +1417,105 @@ class HTTP_Response $this->_listeners[$id]->update($this, $event, $data); } } + + + /** + * Decodes the message-body encoded by gzip + * + * The real decoding work is done by gzinflate() built-in function, this + * method only parses the header and checks data for compliance with + * RFC 1952 + * + * @access private + * @param string gzip-encoded data + * @return string decoded data + */ + function _decodeGzip($data) + { + if (HTTP_REQUEST_MBSTRING) { + $oldEncoding = mb_internal_encoding(); + mb_internal_encoding('iso-8859-1'); + } + $length = strlen($data); + // If it doesn't look like gzip-encoded data, don't bother + if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) { + return $data; + } + $method = ord(substr($data, 2, 1)); + if (8 != $method) { + return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD); + } + $flags = ord(substr($data, 3, 1)); + if ($flags & 224) { + return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA); + } + + // header is 10 bytes minimum. may be longer, though. + $headerLength = 10; + // extra fields, need to skip 'em + if ($flags & 4) { + if ($length - $headerLength - 2 < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $extraLength = unpack('v', substr($data, 10, 2)); + if ($length - $headerLength - 2 - $extraLength[1] < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $headerLength += $extraLength[1] + 2; + } + // file name, need to skip that + if ($flags & 8) { + if ($length - $headerLength - 1 < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $filenameLength = strpos(substr($data, $headerLength), chr(0)); + if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $headerLength += $filenameLength + 1; + } + // comment, need to skip that also + if ($flags & 16) { + if ($length - $headerLength - 1 < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $commentLength = strpos(substr($data, $headerLength), chr(0)); + if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $headerLength += $commentLength + 1; + } + // have a CRC for header. let's check + if ($flags & 1) { + if ($length - $headerLength - 2 < 8) { + return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); + } + $crcReal = 0xffff & crc32(substr($data, 0, $headerLength)); + $crcStored = unpack('v', substr($data, $headerLength, 2)); + if ($crcReal != $crcStored[1]) { + return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC); + } + $headerLength += 2; + } + // unpacked data CRC and size at the end of encoded data + $tmp = unpack('V2', substr($data, -8)); + $dataCrc = $tmp[1]; + $dataSize = $tmp[2]; + + // finally, call the gzinflate() function + // don't pass $dataSize to gzinflate, see bugs #13135, #14370 + $unpacked = gzinflate(substr($data, $headerLength, -8)); + if (false === $unpacked) { + return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ); + } elseif ($dataSize != strlen($unpacked)) { + return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ); + } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) { + return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC); + } + if (HTTP_REQUEST_MBSTRING) { + mb_internal_encoding($oldEncoding); + } + return $unpacked; + } } // End class HTTP_Response ?> diff --git a/thirdparty/pear/HTTP/Request/Listener.php b/thirdparty/pear/HTTP/Request/Listener.php old mode 100644 new mode 100755 index 4923419..c9095eb --- a/thirdparty/pear/HTTP/Request/Listener.php +++ b/thirdparty/pear/HTTP/Request/Listener.php @@ -1,48 +1,58 @@ | -// +-----------------------------------------------------------------------+ -// -// $Id$ -// +/** + * Listener for HTTP_Request and HTTP_Response objects + * + * PHP versions 4 and 5 + * + * LICENSE: + * + * Copyright (c) 2002-2007, Richard Heyes + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * o Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * o The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @category HTTP + * @package HTTP_Request + * @author Alexey Borzov + * @copyright 2002-2007 Richard Heyes + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: Listener.php,v 1.3 2007/05/18 10:33:31 avb Exp $ + * @link http://pear.php.net/package/HTTP_Request/ + */ /** + * Listener for HTTP_Request and HTTP_Response objects + * * This class implements the Observer part of a Subject-Observer - * design pattern. It listens to the events sent by a - * HTTP_Request or HTTP_Response instance. + * design pattern. * - * @package HTTP_Request - * @author Alexey Borzov - * @version $Revision$ + * @category HTTP + * @package HTTP_Request + * @author Alexey Borzov + * @version Release: 1.4.4 */ class HTTP_Request_Listener { diff --git a/thirdparty/pear/HTTP/Upload.php b/thirdparty/pear/HTTP/Upload.php old mode 100644 new mode 100755 index d2390e0..d2390e0 --- a/thirdparty/pear/HTTP/Upload.php +++ b/thirdparty/pear/HTTP/Upload.php diff --git a/thirdparty/pear/JSON.php b/thirdparty/pear/JSON.php old mode 100644 new mode 100755 index 8283e7e..8283e7e --- a/thirdparty/pear/JSON.php +++ b/thirdparty/pear/JSON.php diff --git a/thirdparty/pear/Log.php b/thirdparty/pear/Log.php old mode 100644 new mode 100755 index a762882..7a770fc --- a/thirdparty/pear/Log.php +++ b/thirdparty/pear/Log.php @@ -3,27 +3,28 @@ * $Header$ * $Horde: horde/lib/Log.php,v 1.15 2000/06/29 23:39:45 jon Exp $ * - * @version $Revision$ + * @version $Revision: 293929 $ * @package Log */ -define('PEAR_LOG_EMERG', 0); /** System is unusable */ -define('PEAR_LOG_ALERT', 1); /** Immediate action required */ -define('PEAR_LOG_CRIT', 2); /** Critical conditions */ -define('PEAR_LOG_ERR', 3); /** Error conditions */ -define('PEAR_LOG_WARNING', 4); /** Warning conditions */ -define('PEAR_LOG_NOTICE', 5); /** Normal but significant */ -define('PEAR_LOG_INFO', 6); /** Informational */ -define('PEAR_LOG_DEBUG', 7); /** Debug-level messages */ +define('PEAR_LOG_EMERG', 0); /* System is unusable */ +define('PEAR_LOG_ALERT', 1); /* Immediate action required */ +define('PEAR_LOG_CRIT', 2); /* Critical conditions */ +define('PEAR_LOG_ERR', 3); /* Error conditions */ +define('PEAR_LOG_WARNING', 4); /* Warning conditions */ +define('PEAR_LOG_NOTICE', 5); /* Normal but significant */ +define('PEAR_LOG_INFO', 6); /* Informational */ +define('PEAR_LOG_DEBUG', 7); /* Debug-level messages */ -define('PEAR_LOG_ALL', bindec('11111111')); /** All messages */ -define('PEAR_LOG_NONE', bindec('00000000')); /** No message */ +define('PEAR_LOG_ALL', 0xffffffff); /* All messages */ +define('PEAR_LOG_NONE', 0x00000000); /* No message */ /* Log types for PHP's native error_log() function. */ -define('PEAR_LOG_TYPE_SYSTEM', 0); /** Use PHP's system logger */ -define('PEAR_LOG_TYPE_MAIL', 1); /** Use PHP's mail() function */ -define('PEAR_LOG_TYPE_DEBUG', 2); /** Use PHP's debugging connection */ -define('PEAR_LOG_TYPE_FILE', 3); /** Append to a file */ +define('PEAR_LOG_TYPE_SYSTEM', 0); /* Use PHP's system logger */ +define('PEAR_LOG_TYPE_MAIL', 1); /* Use PHP's mail() function */ +define('PEAR_LOG_TYPE_DEBUG', 2); /* Use PHP's debugging connection */ +define('PEAR_LOG_TYPE_FILE', 3); /* Append to a file */ +define('PEAR_LOG_TYPE_SAPI', 4); /* Use the SAPI logging handler */ /** * The Log:: class implements both an abstraction for various logging @@ -40,7 +41,7 @@ class Log * Indicates whether or not the log can been opened / connected. * * @var boolean - * @access private + * @access protected */ var $_opened = false; @@ -48,7 +49,7 @@ class Log * Instance-specific unique identification number. * * @var integer - * @access private + * @access protected */ var $_id = 0; @@ -56,7 +57,7 @@ class Log * The label that uniquely identifies this set of log messages. * * @var string - * @access private + * @access protected */ var $_ident = ''; @@ -64,14 +65,15 @@ class Log * The default priority to use when logging an event. * * @var integer - * @access private + * @access protected */ var $_priority = PEAR_LOG_INFO; /** * The bitmask of allowed log levels. + * * @var integer - * @access private + * @access protected */ var $_mask = PEAR_LOG_ALL; @@ -79,10 +81,26 @@ class Log * Holds all Log_observer objects that wish to be notified of new messages. * * @var array - * @access private + * @access protected */ var $_listeners = array(); + /** + * Maps canonical format keys to position arguments for use in building + * "line format" strings. + * + * @var array + * @access protected + */ + var $_formatMap = array('%{timestamp}' => '%1$s', + '%{ident}' => '%2$s', + '%{priority}' => '%3$s', + '%{message}' => '%4$s', + '%{file}' => '%5$s', + '%{line}' => '%6$s', + '%{function}' => '%7$s', + '%{class}' => '%8$s', + '%\{' => '%%{'); /** * Attempts to return a concrete Log instance of type $handler. @@ -104,8 +122,8 @@ class Log * * @param int $level Log messages up to and including this level. * - * @return object Log The newly created concrete Log instance, or an - * false on an error. + * @return object Log The newly created concrete Log instance, or + * null on an error. * @access public * @since Log 1.0 */ @@ -121,14 +139,18 @@ class Log * a failure as fatal. The caller may have already included their own * version of the named class. */ - @include_once $classfile; + if (!class_exists($class, false)) { + include_once $classfile; + } /* If the class exists, return a new instance of it. */ - if (class_exists($class)) { - return new $class($name, $ident, $conf, $level); + if (class_exists($class, false)) { + $obj = new $class($name, $ident, $conf, $level); + return $obj; } - return false; + $null = null; + return $null; } /** @@ -162,8 +184,8 @@ class Log * * @param int $level Log messages up to and including this level. * - * @return object Log The newly created concrete Log instance, or an - * false on an error. + * @return object Log The newly created concrete Log instance, or + * null on an error. * @access public * @since Log 1.0 */ @@ -369,7 +391,7 @@ class Log * * @return string The string representation of the message. * - * @access private + * @access protected */ function _extractMessage($message) { @@ -388,14 +410,20 @@ class Log } else if (method_exists($message, '__tostring')) { $message = (string)$message; } else { - $message = print_r($message, true); + $message = var_export($message, true); } } else if (is_array($message)) { if (isset($message['message'])) { - $message = $message['message']; + if (is_scalar($message['message'])) { + $message = $message['message']; + } else { + $message = var_export($message['message'], true); + } } else { - $message = print_r($message, true); + $message = var_export($message, true); } + } else if (is_bool($message) || $message === NULL) { + $message = var_export($message, true); } /* Otherwise, we assume the message is a string. */ @@ -403,12 +431,112 @@ class Log } /** + * Using debug_backtrace(), returns the file, line, and enclosing function + * name of the source code context from which log() was invoked. + * + * @param int $depth The initial number of frames we should step + * back into the trace. + * + * @return array Array containing four strings: the filename, the line, + * the function name, and the class name from which log() + * was called. + * + * @access private + * @since Log 1.9.4 + */ + function _getBacktraceVars($depth) + { + /* Start by generating a backtrace from the current call (here). */ + $bt = debug_backtrace(); + + /* + * If we were ultimately invoked by the composite handler, we need to + * increase our depth one additional level to compensate. + */ + $class = isset($bt[$depth+1]['class']) ? $bt[$depth+1]['class'] : null; + if ($class !== null && strcasecmp($class, 'Log_composite') == 0) { + $depth++; + $class = isset($bt[$depth + 1]) ? $bt[$depth + 1]['class'] : null; + } + + /* + * We're interested in the frame which invoked the log() function, so + * we need to walk back some number of frames into the backtrace. The + * $depth parameter tells us where to start looking. We go one step + * further back to find the name of the encapsulating function from + * which log() was called. + */ + $file = isset($bt[$depth]) ? $bt[$depth]['file'] : null; + $line = isset($bt[$depth]) ? $bt[$depth]['line'] : 0; + $func = isset($bt[$depth + 1]) ? $bt[$depth + 1]['function'] : null; + + /* + * However, if log() was called from one of our "shortcut" functions, + * we're going to need to go back an additional step. + */ + if (in_array($func, array('emerg', 'alert', 'crit', 'err', 'warning', + 'notice', 'info', 'debug'))) { + $file = isset($bt[$depth + 1]) ? $bt[$depth + 1]['file'] : null; + $line = isset($bt[$depth + 1]) ? $bt[$depth + 1]['line'] : 0; + $func = isset($bt[$depth + 2]) ? $bt[$depth + 2]['function'] : null; + $class = isset($bt[$depth + 2]) ? $bt[$depth + 2]['class'] : null; + } + + /* + * If we couldn't extract a function name (perhaps because we were + * executed from the "main" context), provide a default value. + */ + if (is_null($func)) { + $func = '(none)'; + } + + /* Return a 4-tuple containing (file, line, function, class). */ + return array($file, $line, $func, $class); + } + + /** + * Produces a formatted log line based on a format string and a set of + * variables representing the current log record and state. + * + * @return string Formatted log string. + * + * @access protected + * @since Log 1.9.4 + */ + function _format($format, $timestamp, $priority, $message) + { + /* + * If the format string references any of the backtrace-driven + * variables (%5 %6,%7,%8), generate the backtrace and fetch them. + */ + if (preg_match('/%[5678]/', $format)) { + list($file, $line, $func, $class) = $this->_getBacktraceVars(2); + } + + /* + * Build the formatted string. We use the sprintf() function's + * "argument swapping" capability to dynamically select and position + * the variables which will ultimately appear in the log string. + */ + return sprintf($format, + $timestamp, + $this->_ident, + $this->priorityToString($priority), + $message, + isset($file) ? $file : '', + isset($line) ? $line : '', + isset($func) ? $func : '', + isset($class) ? $class : ''); + } + + /** * Returns the string representation of a PEAR_LOG_* integer constant. * * @param int $priority A PEAR_LOG_* integer constant. * * @return string The string representation of $level. * + * @access public * @since Log 1.0 */ function priorityToString($priority) @@ -428,8 +556,39 @@ class Log } /** + * Returns the the PEAR_LOG_* integer constant for the given string + * representation of a priority name. This function performs a + * case-insensitive search. + * + * @param string $name String containing a priority name. + * + * @return string The PEAR_LOG_* integer contstant corresponding + * the the specified priority name. + * + * @access public + * @since Log 1.9.0 + */ + function stringToPriority($name) + { + $levels = array( + 'emergency' => PEAR_LOG_EMERG, + 'alert' => PEAR_LOG_ALERT, + 'critical' => PEAR_LOG_CRIT, + 'error' => PEAR_LOG_ERR, + 'warning' => PEAR_LOG_WARNING, + 'notice' => PEAR_LOG_NOTICE, + 'info' => PEAR_LOG_INFO, + 'debug' => PEAR_LOG_DEBUG + ); + + return $levels[strtolower($name)]; + } + + /** * Calculate the log mask for the given priority. * + * This method may be called statically. + * * @param integer $priority The priority whose mask will be calculated. * * @return integer The calculated log mask. @@ -445,15 +604,57 @@ class Log /** * Calculate the log mask for all priorities up to the given priority. * + * This method may be called statically. + * * @param integer $priority The maximum priority covered by this mask. * - * @return integer The calculated log mask. + * @return integer The resulting log mask. * * @access public * @since Log 1.7.0 + * + * @deprecated deprecated since Log 1.9.4; use Log::MAX() instead */ function UPTO($priority) { + return Log::MAX($priority); + } + + /** + * Calculate the log mask for all priorities greater than or equal to the + * given priority. In other words, $priority will be the lowest priority + * matched by the resulting mask. + * + * This method may be called statically. + * + * @param integer $priority The minimum priority covered by this mask. + * + * @return integer The resulting log mask. + * + * @access public + * @since Log 1.9.4 + */ + function MIN($priority) + { + return PEAR_LOG_ALL ^ ((1 << $priority) - 1); + } + + /** + * Calculate the log mask for all priorities less than or equal to the + * given priority. In other words, $priority will be the highests priority + * matched by the resulting mask. + * + * This method may be called statically. + * + * @param integer $priority The maximum priority covered by this mask. + * + * @return integer The resulting log mask. + * + * @access public + * @since Log 1.9.4 + */ + function MAX($priority) + { return ((1 << ($priority + 1)) - 1); } @@ -495,7 +696,7 @@ class Log * @return boolean True if the given priority is included in the current * log mask. * - * @access private + * @access protected * @since Log 1.7.0 */ function _isMasked($priority) @@ -581,7 +782,7 @@ class Log * * @param array $event A hash describing the log event. * - * @access private + * @access protected */ function _announce($event) { @@ -631,5 +832,3 @@ class Log return $this->_ident; } } - -?> diff --git a/thirdparty/pear/Log/composite.php b/thirdparty/pear/Log/composite.php old mode 100644 new mode 100755 index 3b0726c..f8a8a95 --- a/thirdparty/pear/Log/composite.php +++ b/thirdparty/pear/Log/composite.php @@ -3,7 +3,7 @@ * $Header$ * $Horde: horde/lib/Log/composite.php,v 1.2 2000/06/28 21:36:13 jon Exp $ * - * @version $Revision$ + * @version $Revision: 215528 $ * @package Log */ @@ -42,58 +42,77 @@ class Log_composite extends Log * * @access public */ - function Log_composite($name = false, $ident = false, $conf = false, + function Log_composite($name, $ident = '', $conf = array(), $level = PEAR_LOG_DEBUG) { + $this->_ident = $ident; } /** - * Opens the child connections. + * Opens all of the child instances. + * + * @return True if all of the child instances were successfully opened. * * @access public */ function open() { - if (!$this->_opened) { - foreach ($this->_children as $id => $child) { - $this->_children[$id]->open(); - } - $this->_opened = true; + /* Attempt to open each of our children. */ + $this->_opened = true; + foreach ($this->_children as $id => $child) { + $this->_opened &= $this->_children[$id]->open(); } + + /* If all children were opened, return success. */ + return $this->_opened; } /** - * Closes any child instances. + * Closes all of the child instances. + * + * @return True if all of the child instances were successfully closed. * * @access public */ function close() { - if ($this->_opened) { - foreach ($this->_children as $id => $child) { - $this->_children[$id]->close(); - } - $this->_opened = false; + /* Attempt to close each of our children. */ + $closed = true; + foreach ($this->_children as $id => $child) { + $closed &= $this->_children[$id]->close(); } + + /* Track the _opened state for consistency. */ + $this->_opened = false; + + /* If all children were closed, return success. */ + return $closed; } /** - * Flushes all open child instances. + * Flushes all child instances. It is assumed that all of the children + * have been successfully opened. + * + * @return True if all of the child instances were successfully flushed. * * @access public * @since Log 1.8.2 */ function flush() { - if ($this->_opened) { - foreach ($this->_children as $id => $child) { - $this->_children[$id]->flush(); - } + /* Attempt to flush each of our children. */ + $flushed = true; + foreach ($this->_children as $id => $child) { + $flushed &= $this->_children[$id]->flush(); } + + /* If all children were flushed, return success. */ + return $flushed; } /** - * Sends $message and $priority to each child of this composite. + * Sends $message and $priority to each child of this composite. If the + * children aren't already open, they will be opened here. * * @param mixed $message String or object containing the message * to log. @@ -115,13 +134,26 @@ class Log_composite extends Log $priority = $this->_priority; } + /* + * If the handlers haven't been opened, attempt to open them now. + * However, we don't treat failure to open all of the handlers as a + * fatal error. We defer that consideration to the success of calling + * each handler's log() method below. + */ + if (!$this->_opened) { + $this->open(); + } + + /* Attempt to log the event using each of the children. */ + $success = true; foreach ($this->_children as $id => $child) { - $this->_children[$id]->log($message, $priority); + $success &= $this->_children[$id]->log($message, $priority); } $this->_announce(array('priority' => $priority, 'message' => $message)); - return true; + /* Return success if all of the children logged the event. */ + return $success; } /** @@ -146,6 +178,10 @@ class Log_composite extends Log */ function setIdent($ident) { + /* Call our base class's setIdent() method. */ + parent::setIdent($ident); + + /* ... and then call setIdent() on all of our children. */ foreach ($this->_children as $id => $child) { $this->_children[$id]->setIdent($ident); } @@ -191,6 +227,5 @@ class Log_composite extends Log return true; } -} -?> +} diff --git a/thirdparty/pear/Log/console.php b/thirdparty/pear/Log/console.php old mode 100644 new mode 100755 index 01c4f84..3a735a0 --- a/thirdparty/pear/Log/console.php +++ b/thirdparty/pear/Log/console.php @@ -2,7 +2,7 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 224513 $ * @package Log */ @@ -56,18 +56,6 @@ class Log_console extends Log var $_timeFormat = '%b %d %H:%M:%S'; /** - * Hash that maps canonical format keys to position arguments for the - * "line format" string. - * @var array - * @access private - */ - var $_formatMap = array('%{timestamp}' => '%1$s', - '%{ident}' => '%2$s', - '%{priority}' => '%3$s', - '%{message}' => '%4$s', - '%\{' => '%%{'); - - /** * Constructs a new Log_console object. * * @param string $name Ignored. @@ -115,7 +103,34 @@ class Log_console extends Log */ function _Log_console() { + $this->close(); + } + + /** + * Open the output stream. + * + * @access public + * @since Log 1.9.7 + */ + function open() + { + $this->_opened = true; + return true; + } + + /** + * Closes the output stream. + * + * This results in a call to flush(). + * + * @access public + * @since Log 1.9.0 + */ + function close() + { $this->flush(); + $this->_opened = false; + return true; } /** @@ -135,7 +150,11 @@ class Log_console extends Log $this->_buffer = ''; } - return fflush($this->_stream); + if (is_resource($this->_stream)) { + return fflush($this->_stream); + } + + return false; } /** @@ -166,9 +185,9 @@ class Log_console extends Log $message = $this->_extractMessage($message); /* Build the string containing the complete log line. */ - $line = sprintf($this->_lineFormat, strftime($this->_timeFormat), - $this->_ident, $this->priorityToString($priority), - $message) . "\n"; + $line = $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, $message) . "\n"; /* * If buffering is enabled, append this line to the output buffer. @@ -185,6 +204,5 @@ class Log_console extends Log return true; } -} -?> +} diff --git a/thirdparty/pear/Log/daemon.php b/thirdparty/pear/Log/daemon.php old mode 100644 new mode 100755 index 5a81706..418173d --- a/thirdparty/pear/Log/daemon.php +++ b/thirdparty/pear/Log/daemon.php @@ -1,5 +1,10 @@ - * @version $Revision$ + * @version $Revision: 250926 $ * @package Log */ -class Log_daemon extends Log { - +class Log_daemon extends Log +{ /** * Integer holding the log facility to use. * @var string @@ -226,4 +231,5 @@ class Log_daemon extends Log { return $priorities[$priority]; } + } diff --git a/thirdparty/pear/Log/display.php b/thirdparty/pear/Log/display.php old mode 100644 new mode 100755 index 9d8a849..b757f6c --- a/thirdparty/pear/Log/display.php +++ b/thirdparty/pear/Log/display.php @@ -2,7 +2,7 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 255603 $ * @package Log */ @@ -22,19 +22,20 @@ class Log_display extends Log { /** - * String to output before an error message + * String containing the format of a log line. * @var string * @access private */ - var $_error_prepend = ''; + var $_lineFormat = '%3$s: %4$s'; /** - * String to output after an error message + * String containing the timestamp format. It will be passed directly to + * strftime(). Note that the timestamp string will generated using the + * current locale. * @var string * @access private */ - var $_error_append = ''; - + var $_timeFormat = '%b %d %H:%M:%S'; /** * Constructs a new Log_display object. @@ -52,17 +53,70 @@ class Log_display extends Log $this->_ident = $ident; $this->_mask = Log::UPTO($level); - if (!empty($conf['error_prepend'])) { - $this->_error_prepend = $conf['error_prepend']; + /* Start by configuring the line format. */ + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + /* We may need to prepend a string to our line format. */ + $prepend = null; + if (isset($conf['error_prepend'])) { + $prepend = $conf['error_prepend']; } else { - $this->_error_prepend = ini_get('error_prepend_string'); + $prepend = ini_get('error_prepend_string'); + } + if (!empty($prepend)) { + $this->_lineFormat = $prepend . $this->_lineFormat; } - if (!empty($conf['error_append'])) { - $this->_error_append = $conf['error_append']; + /* We may also need to append a string to our line format. */ + $append = null; + if (isset($conf['error_append'])) { + $append = $conf['error_append']; } else { - $this->_error_append = ini_get('error_append_string'); + $append = ini_get('error_append_string'); } + if (!empty($append)) { + $this->_lineFormat .= $append; + } + + /* Lastly, the line ending sequence is also configurable. */ + if (isset($conf['linebreak'])) { + $this->_lineFormat .= $conf['linebreak']; + } else { + $this->_lineFormat .= "
    \n"; + } + + /* The user can also change the time format. */ + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + } + + /** + * Opens the display handler. + * + * @access public + * @since Log 1.9.6 + */ + function open() + { + $this->_opened = true; + return true; + } + + /** + * Closes the display handler. + * + * @access public + * @since Log 1.9.6 + */ + function close() + { + $this->_opened = false; + return true; } /** @@ -93,16 +147,15 @@ class Log_display extends Log $message = $this->_extractMessage($message); /* Build and output the complete log line. */ - echo $this->_error_prepend . - '' . ucfirst($this->priorityToString($priority)) . ': '. - nl2br(htmlspecialchars($message)) . - $this->_error_append . "
    \n"; + echo $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, + nl2br(htmlspecialchars($message))); /* Notify observers about this log message. */ $this->_announce(array('priority' => $priority, 'message' => $message)); return true; } -} -?> +} diff --git a/thirdparty/pear/Log/error_log.php b/thirdparty/pear/Log/error_log.php old mode 100644 new mode 100755 index b432de6..05faf87 --- a/thirdparty/pear/Log/error_log.php +++ b/thirdparty/pear/Log/error_log.php @@ -2,18 +2,18 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 293927 $ * @package Log */ /** * The Log_error_log class is a concrete implementation of the Log abstract * class that logs messages using PHP's error_log() function. - * + * * @author Jon Parise * @since Log 1.7.0 * @package Log - * + * * @example error_log.php Using the error_log handler. */ class Log_error_log extends Log @@ -41,9 +41,25 @@ class Log_error_log extends Log var $_extra_headers = ''; /** + * String containing the format of a log line. + * @var string + * @access private + */ + var $_lineFormat = '%2$s: %4$s'; + + /** + * String containing the timestamp format. It will be passed directly to + * strftime(). Note that the timestamp string will generated using the + * current locale. + * @var string + * @access private + */ + var $_timeFormat = '%b %d %H:%M:%S'; + + /** * Constructs a new Log_error_log object. - * - * @param string $name Ignored. + * + * @param string $name One of the PEAR_LOG_TYPE_* constants. * @param string $ident The identity string. * @param array $conf The configuration array. * @param int $level Log messages up to and including this level. @@ -60,15 +76,50 @@ class Log_error_log extends Log if (!empty($conf['destination'])) { $this->_destination = $conf['destination']; } + if (!empty($conf['extra_headers'])) { $this->_extra_headers = $conf['extra_headers']; } + + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + } + + /** + * Opens the handler. + * + * @access public + * @since Log 1.9.6 + */ + function open() + { + $this->_opened = true; + return true; + } + + /** + * Closes the handler. + * + * @access public + * @since Log 1.9.6 + */ + function close() + { + $this->_opened = false; + return true; } /** * Logs $message using PHP's error_log() function. The message is also * passed along to any Log_observer instances that are observing this Log. - * + * * @param mixed $message String or object containing the message to log. * @param string $priority The priority of the message. Valid * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, @@ -92,13 +143,18 @@ class Log_error_log extends Log /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); - $success = error_log($this->_ident . ': ' . $message, $this->_type, - $this->_destination, $this->_extra_headers); + /* Build the string containing the complete log line. */ + $line = $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, $message); + + /* Pass the log line and parameters to the error_log() function. */ + $success = error_log($line, $this->_type, $this->_destination, + $this->_extra_headers); $this->_announce(array('priority' => $priority, 'message' => $message)); return $success; } -} -?> +} diff --git a/thirdparty/pear/Log/file.php b/thirdparty/pear/Log/file.php old mode 100644 new mode 100755 index 4ac38ad..9e84eb3 --- a/thirdparty/pear/Log/file.php +++ b/thirdparty/pear/Log/file.php @@ -2,7 +2,7 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 224513 $ * @package Log */ @@ -42,6 +42,13 @@ class Log_file extends Log var $_append = true; /** + * Should advisory file locking (i.e., flock()) be used? + * @var boolean + * @access private + */ + var $_locking = false; + + /** * Integer (in octal) containing the log file's permissions mode. * @var integer * @access private @@ -49,6 +56,14 @@ class Log_file extends Log var $_mode = 0644; /** + * Integer (in octal) specifying the file permission mode that will be + * used when creating directories that do not already exist. + * @var integer + * @access private + */ + var $_dirmode = 0755; + + /** * String containing the format of a log line. * @var string * @access private @@ -65,18 +80,6 @@ class Log_file extends Log var $_timeFormat = '%b %d %H:%M:%S'; /** - * Hash that maps canonical format keys to position arguments for the - * "line format" string. - * @var array - * @access private - */ - var $_formatMap = array('%{timestamp}' => '%1$s', - '%{ident}' => '%2$s', - '%{priority}' => '%3$s', - '%{message}' => '%4$s', - '%\{' => '%%{'); - - /** * String containing the end-on-line character sequence. * @var string * @access private @@ -104,8 +107,24 @@ class Log_file extends Log $this->_append = $conf['append']; } + if (isset($conf['locking'])) { + $this->_locking = $conf['locking']; + } + if (!empty($conf['mode'])) { - $this->_mode = $conf['mode']; + if (is_string($conf['mode'])) { + $this->_mode = octdec($conf['mode']); + } else { + $this->_mode = $conf['mode']; + } + } + + if (!empty($conf['dirmode'])) { + if (is_string($conf['dirmode'])) { + $this->_dirmode = octdec($conf['dirmode']); + } else { + $this->_dirmode = $conf['dirmode']; + } } if (!empty($conf['lineFormat'])) { @@ -141,6 +160,8 @@ class Log_file extends Log * Creates the given directory path. If the parent directories don't * already exist, they will be created, too. * + * This implementation is inspired by Python's os.makedirs function. + * * @param string $path The full directory path to create. * @param integer $mode The permissions mode with which the * directories will be created. @@ -152,33 +173,23 @@ class Log_file extends Log */ function _mkpath($path, $mode = 0700) { - static $depth = 0; - - /* Guard against potentially infinite recursion. */ - if ($depth++ > 25) { - trigger_error("_mkpath(): Maximum recursion depth (25) exceeded", - E_USER_WARNING); - return false; + /* Separate the last pathname component from the rest of the path. */ + $head = dirname($path); + $tail = basename($path); + + /* Make sure we've split the path into two complete components. */ + if (empty($tail)) { + $head = dirname($path); + $tail = basename($path); } - /* We're only interested in the directory component of the path. */ - $path = dirname($path); - - /* If the directory already exists, return success immediately. */ - if (is_dir($path)) { - $depth = 0; - return true; + /* Recurse up the path if our current segment does not exist. */ + if (!empty($head) && !empty($tail) && !is_dir($head)) { + $this->_mkpath($head, $mode); } - /* - * In order to understand recursion, you must first understand - * recursion ... - */ - if ($this->_mkpath($path, $mode) === false) { - return false; - } - - return @mkdir($path, $mode); + /* Create this segment of the path. */ + return @mkdir($head, $mode); } /** @@ -195,16 +206,22 @@ class Log_file extends Log if (!$this->_opened) { /* If the log file's directory doesn't exist, create it. */ if (!is_dir(dirname($this->_filename))) { - $this->_mkpath($this->_filename); + $this->_mkpath($this->_filename, $this->_dirmode); } + /* Determine whether the log file needs to be created. */ + $creating = !file_exists($this->_filename); + /* Obtain a handle to the log file. */ $this->_fp = fopen($this->_filename, ($this->_append) ? 'a' : 'w'); + /* We consider the file "opened" if we have a valid file pointer. */ $this->_opened = ($this->_fp !== false); - /* Attempt to set the log file's mode. */ - @chmod($this->_filename, $this->_mode); + /* Attempt to set the file's permissions if we just created it. */ + if ($creating && $this->_opened) { + chmod($this->_filename, $this->_mode); + } } return $this->_opened; @@ -233,7 +250,11 @@ class Log_file extends Log */ function flush() { - return fflush($this->_fp); + if (is_resource($this->_fp)) { + return fflush($this->_fp); + } + + return false; } /** @@ -269,18 +290,27 @@ class Log_file extends Log $message = $this->_extractMessage($message); /* Build the string containing the complete log line. */ - $line = sprintf($this->_lineFormat, strftime($this->_timeFormat), - $this->_ident, $this->priorityToString($priority), - $message) . $this->_eol; + $line = $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, $message) . $this->_eol; + + /* If locking is enabled, acquire an exclusive lock on the file. */ + if ($this->_locking) { + flock($this->_fp, LOCK_EX); + } /* Write the log line to the log file. */ $success = (fwrite($this->_fp, $line) !== false); + /* Unlock the file now that we're finished writing to it. */ + if ($this->_locking) { + flock($this->_fp, LOCK_UN); + } + /* Notify observers about this log message. */ $this->_announce(array('priority' => $priority, 'message' => $message)); return $success; } -} -?> +} diff --git a/thirdparty/pear/Log/firebug.php b/thirdparty/pear/Log/firebug.php new file mode 100644 index 0000000..ee6da62 --- /dev/null +++ b/thirdparty/pear/Log/firebug.php @@ -0,0 +1,214 @@ + + * @since Log 1.9.11 + * @package Log + * + * @example firebug.php Using the firebug handler. + */ +class Log_firebug extends Log +{ + /** + * Should the output be buffered or displayed immediately? + * @var string + * @access private + */ + var $_buffering = false; + + /** + * String holding the buffered output. + * @var string + * @access private + */ + var $_buffer = array(); + + /** + * String containing the format of a log line. + * @var string + * @access private + */ + var $_lineFormat = '%2$s [%3$s] %4$s'; + + /** + * String containing the timestamp format. It will be passed directly to + * strftime(). Note that the timestamp string will generated using the + * current locale. + * + * Note! Default lineFormat of this driver does not display time. + * + * @var string + * @access private + */ + var $_timeFormat = '%b %d %H:%M:%S'; + + /** + * Mapping of log priorities to Firebug methods. + * @var array + * @access private + */ + var $_methods = array( + PEAR_LOG_EMERG => 'error', + PEAR_LOG_ALERT => 'error', + PEAR_LOG_CRIT => 'error', + PEAR_LOG_ERR => 'error', + PEAR_LOG_WARNING => 'warn', + PEAR_LOG_NOTICE => 'info', + PEAR_LOG_INFO => 'info', + PEAR_LOG_DEBUG => 'debug' + ); + + /** + * Constructs a new Log_firebug object. + * + * @param string $name Ignored. + * @param string $ident The identity string. + * @param array $conf The configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_firebug($name = '', $ident = 'PHP', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_ident = $ident; + $this->_mask = Log::UPTO($level); + if (isset($conf['buffering'])) { + $this->_buffering = $conf['buffering']; + } + + if ($this->_buffering) { + register_shutdown_function(array(&$this, '_Log_firebug')); + } + + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + } + + /** + * Opens the firebug handler. + * + * @access public + */ + function open() + { + $this->_opened = true; + return true; + } + + /** + * Destructor + */ + function _Log_firebug() + { + $this->close(); + } + + /** + * Closes the firebug handler. + * + * @access public + */ + function close() + { + $this->flush(); + $this->_opened = false; + return true; + } + + /** + * Flushes all pending ("buffered") data. + * + * @access public + */ + function flush() { + if (count($this->_buffer)) { + print '\n"; + }; + $this->_buffer = array(); + } + + /** + * Writes $message to Firebug console. Also, passes the message + * along to any Log_observer instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + $method = $this->_methods[$priority]; + + /* normalize line breaks */ + $message = str_replace("\r\n", "\n", $message); + + /* escape line breaks */ + $message = str_replace("\n", "\\n\\\n", $message); + + /* escape quotes */ + $message = str_replace('"', '\\"', $message); + + /* Build the string containing the complete log line. */ + $line = $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, + $message); + + if ($this->_buffering) { + $this->_buffer[] = sprintf('console.%s("%s");', $method, $line); + } else { + print '\n"; + } + /* Notify observers about this log message. */ + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } + +} diff --git a/thirdparty/pear/Log/mail.php b/thirdparty/pear/Log/mail.php old mode 100644 new mode 100755 index 44633e5..9f0b29c --- a/thirdparty/pear/Log/mail.php +++ b/thirdparty/pear/Log/mail.php @@ -2,7 +2,7 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 266658 $ * @package Log */ @@ -11,13 +11,13 @@ * which sends log messages to a mailbox. * The mail is actually sent when you close() the logger, or when the destructor * is called (when the script is terminated). - * + * * PLEASE NOTE that you must create a Log_mail object using =&, like this : * $logger =& Log::factory("mail", "recipient@example.com", ...) - * + * * This is a PEAR requirement for destructors to work properly. * See http://pear.php.net/manual/en/class.pear.php - * + * * @author Ronnie Garcia * @author Jon Parise * @since Log 1.3 @@ -27,21 +27,22 @@ */ class Log_mail extends Log { - /** - * String holding the recipient's email address. + /** + * String holding the recipients' email addresses. Multiple addresses + * should be separated with commas. * @var string * @access private */ - var $_recipient = ''; + var $_recipients = ''; - /** + /** * String holding the sender's email address. * @var string * @access private */ var $_from = ''; - /** + /** * String holding the email's subject. * @var string * @access private @@ -56,21 +57,60 @@ class Log_mail extends Log var $_preamble = ''; /** + * String containing the format of a log line. + * @var string + * @access private + */ + var $_lineFormat = '%1$s %2$s [%3$s] %4$s'; + + /** + * String containing the timestamp format. It will be passed directly to + * strftime(). Note that the timestamp string will generated using the + * current locale. + * @var string + * @access private + */ + var $_timeFormat = '%b %d %H:%M:%S'; + + /** * String holding the mail message body. * @var string * @access private */ var $_message = ''; + /** + * Flag used to indicated that log lines have been written to the message + * body and the message should be sent on close(). + * @var boolean + * @access private + */ + var $_shouldSend = false; + + /** + * String holding the backend name of PEAR::Mail + * @var string + * @access private + */ + var $_mailBackend = ''; + + /** + * Array holding the params for PEAR::Mail + * @var array + * @access private + */ + var $_mailParams = array(); /** * Constructs a new Log_mail object. - * + * * Here is how you can customize the mail driver with the conf[] hash : - * $conf['from'] : the mail's "From" header line, - * $conf['subject'] : the mail's "Subject" line. - * - * @param string $name The filename of the logfile. + * $conf['from']: the mail's "From" header line, + * $conf['subject']: the mail's "Subject" line. + * $conf['mailBackend']: backend name of PEAR::Mail + * $conf['mailParams']: parameters for the PEAR::Mail backend + * + * @param string $name The message's recipients. * @param string $ident The identity string. * @param array $conf The configuration array. * @param int $level Log messages up to and including this level. @@ -80,7 +120,7 @@ class Log_mail extends Log $level = PEAR_LOG_DEBUG) { $this->_id = md5(microtime()); - $this->_recipient = $name; + $this->_recipients = $name; $this->_ident = $ident; $this->_mask = Log::UPTO($level); @@ -89,7 +129,7 @@ class Log_mail extends Log } else { $this->_from = ini_get('sendmail_from'); } - + if (!empty($conf['subject'])) { $this->_subject = $conf['subject']; } @@ -97,11 +137,29 @@ class Log_mail extends Log if (!empty($conf['preamble'])) { $this->_preamble = $conf['preamble']; } - + + if (!empty($conf['lineFormat'])) { + $this->_lineFormat = str_replace(array_keys($this->_formatMap), + array_values($this->_formatMap), + $conf['lineFormat']); + } + + if (!empty($conf['timeFormat'])) { + $this->_timeFormat = $conf['timeFormat']; + } + + if (!empty($conf['mailBackend'])) { + $this->_mailBackend = $conf['mailBackend']; + } + + if (!empty($conf['mailParams'])) { + $this->_mailParams = $conf['mailParams']; + } + /* register the destructor */ register_shutdown_function(array(&$this, '_Log_mail')); } - + /** * Destructor. Calls close(). * @@ -115,16 +173,17 @@ class Log_mail extends Log /** * Starts a new mail message. * This is implicitly called by log(), if necessary. - * + * * @access public */ function open() { if (!$this->_opened) { if (!empty($this->_preamble)) { - $this->_message = $this->_preamble . "\n\n"; + $this->_message = $this->_preamble . "\r\n\r\n"; } $this->_opened = true; + $_shouldSend = false; } return $this->_opened; @@ -133,24 +192,38 @@ class Log_mail extends Log /** * Closes the message, if it is open, and sends the mail. * This is implicitly called by the destructor, if necessary. - * + * * @access public */ function close() { if ($this->_opened) { - if (!empty($this->_message)) { - $headers = "From: $this->_from\n"; - $headers .= "User-Agent: Log_mail"; - - if (mail($this->_recipient, $this->_subject, $this->_message, - $headers) == false) { - error_log("Log_mail: Failure executing mail()", 0); - return false; + if ($this->_shouldSend && !empty($this->_message)) { + if ($this->_mailBackend === '') { // use mail() + $headers = "From: $this->_from\r\n"; + $headers .= 'User-Agent: PEAR Log Package'; + if (mail($this->_recipients, $this->_subject, + $this->_message, $headers) == false) { + return false; + } + } else { // use PEAR::Mail + include_once 'Mail.php'; + $headers = array('From' => $this->_from, + 'To' => $this->_recipients, + 'User-Agent' => 'PEAR Log Package', + 'Subject' => $this->_subject); + $mailer = &Mail::factory($this->_mailBackend, + $this->_mailParams); + $res = $mailer->send($this->_recipients, $headers, + $this->_message); + if (PEAR::isError($res)) { + return false; + } } /* Clear the message string now that the email has been sent. */ $this->_message = ''; + $this->_shouldSend = false; } $this->_opened = false; } @@ -178,7 +251,7 @@ class Log_mail extends Log /** * Writes $message to the currently open mail message. * Calls open(), if necessary. - * + * * @param mixed $message String or object containing the message to log. * @param string $priority The priority of the message. Valid * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, @@ -207,16 +280,15 @@ class Log_mail extends Log /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); - $entry = sprintf("%s %s [%s] %s\n", strftime('%b %d %H:%M:%S'), - $this->_ident, Log::priorityToString($priority), - $message); - - $this->_message .= $entry; + /* Append the string containing the complete log line. */ + $this->_message .= $this->_format($this->_lineFormat, + strftime($this->_timeFormat), + $priority, $message) . "\r\n"; + $this->_shouldSend = true; + /* Notify observers about this log message. */ $this->_announce(array('priority' => $priority, 'message' => $message)); return true; } } - -?> diff --git a/thirdparty/pear/Log/mcal.php b/thirdparty/pear/Log/mcal.php old mode 100644 new mode 100755 index 37ef3aa..76788d0 --- a/thirdparty/pear/Log/mcal.php +++ b/thirdparty/pear/Log/mcal.php @@ -3,68 +3,68 @@ * $Header$ * $Horde: horde/lib/Log/mcal.php,v 1.2 2000/06/28 21:36:13 jon Exp $ * - * @version $Revision$ - * @package Log + * @version $Revision: 180836 $ + * @package Log */ /** * The Log_mcal class is a concrete implementation of the Log:: * abstract class which sends messages to a local or remote calendar * store accessed through MCAL. - * + * * @author Chuck Hagenbuch * @since Horde 1.3 * @since Log 1.0 - * @package Log + * @package Log */ -class Log_mcal extends Log { - +class Log_mcal extends Log +{ /** - * holding the calendar specification to connect to. - * @var string - * @access private - */ + * holding the calendar specification to connect to. + * @var string + * @access private + */ var $_calendar = '{localhost/mstore}'; - /** - * holding the username to use. - * @var string - * @access private - */ + /** + * holding the username to use. + * @var string + * @access private + */ var $_username = ''; - /** - * holding the password to use. - * @var string - * @access private - */ + /** + * holding the password to use. + * @var string + * @access private + */ var $_password = ''; - /** - * holding the options to pass to the calendar stream. - * @var integer - * @access private - */ + /** + * holding the options to pass to the calendar stream. + * @var integer + * @access private + */ var $_options = 0; - /** - * ResourceID of the MCAL stream. - * @var string - * @access private - */ + /** + * ResourceID of the MCAL stream. + * @var string + * @access private + */ var $_stream = ''; - /** - * Integer holding the log facility to use. - * @var string - * @access private - */ + /** + * Integer holding the log facility to use. + * @var string + * @access private + */ var $_name = LOG_SYSLOG; /** * Constructs a new Log_mcal object. - * + * * @param string $name The category to use for our events. * @param string $ident The identity string. * @param array $conf The configuration array. @@ -119,7 +119,7 @@ class Log_mcal extends Log { * calendar stream. Calls open() if necessary. Also passes the * message along to any Log_observer instances that are observing * this Log. - * + * * @param mixed $message String or object containing the message to log. * @param string $priority The priority of the message. Valid * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, @@ -166,6 +166,5 @@ class Log_mcal extends Log { return true; } -} -?> +} diff --git a/thirdparty/pear/Log/mdb2.php b/thirdparty/pear/Log/mdb2.php new file mode 100644 index 0000000..6d0a9e4 --- /dev/null +++ b/thirdparty/pear/Log/mdb2.php @@ -0,0 +1,358 @@ + + * @author Jon Parise + * @since Log 1.9.0 + * @package Log + */ +class Log_mdb2 extends Log +{ + /** + * Variable containing the DSN information. + * @var mixed + * @access private + */ + var $_dsn = ''; + + /** + * Array containing our set of DB configuration options. + * @var array + * @access private + */ + var $_options = array('persistent' => true); + + /** + * Object holding the database handle. + * @var object + * @access private + */ + var $_db = null; + + /** + * Resource holding the prepared statement handle. + * @var resource + * @access private + */ + var $_statement = null; + + /** + * Flag indicating that we're using an existing database connection. + * @var boolean + * @access private + */ + var $_existingConnection = false; + + /** + * String holding the database table to use. + * @var string + * @access private + */ + var $_table = 'log_table'; + + /** + * String holding the name of the ID sequence. + * @var string + * @access private + */ + var $_sequence = 'log_id'; + + /** + * Maximum length of the $ident string. This corresponds to the size of + * the 'ident' column in the SQL table. + * @var integer + * @access private + */ + var $_identLimit = 16; + + /** + * Set of field types used in the database table. + * @var array + * @access private + */ + var $_types = array( + 'id' => 'integer', + 'logtime' => 'timestamp', + 'ident' => 'text', + 'priority' => 'text', + 'message' => 'clob' + ); + + /** + * Constructs a new sql logging object. + * + * @param string $name The target SQL table. + * @param string $ident The identification field. + * @param array $conf The connection configuration array. + * @param int $level Log messages up to and including this level. + * @access public + */ + function Log_mdb2($name, $ident = '', $conf = array(), + $level = PEAR_LOG_DEBUG) + { + $this->_id = md5(microtime()); + $this->_table = $name; + $this->_mask = Log::UPTO($level); + + /* If an options array was provided, use it. */ + if (isset($conf['options']) && is_array($conf['options'])) { + $this->_options = $conf['options']; + } + + /* If a specific sequence name was provided, use it. */ + if (!empty($conf['sequence'])) { + $this->_sequence = $conf['sequence']; + } + + /* If a specific sequence name was provided, use it. */ + if (isset($conf['identLimit'])) { + $this->_identLimit = $conf['identLimit']; + } + + /* Now that the ident limit is confirmed, set the ident string. */ + $this->setIdent($ident); + + /* If an existing database connection was provided, use it. */ + if (isset($conf['db'])) { + $this->_db = &$conf['db']; + $this->_existingConnection = true; + $this->_opened = true; + } elseif (isset($conf['singleton'])) { + $this->_db = &MDB2::singleton($conf['singleton'], $this->_options); + $this->_existingConnection = true; + $this->_opened = true; + } else { + $this->_dsn = $conf['dsn']; + } + } + + /** + * Opens a connection to the database, if it has not already + * been opened. This is implicitly called by log(), if necessary. + * + * @return boolean True on success, false on failure. + * @access public + */ + function open() + { + if (!$this->_opened) { + /* Use the DSN and options to create a database connection. */ + $this->_db = &MDB2::connect($this->_dsn, $this->_options); + if (PEAR::isError($this->_db)) { + return false; + } + + /* Create a prepared statement for repeated use in log(). */ + if (!$this->_prepareStatement()) { + return false; + } + + /* We now consider out connection open. */ + $this->_opened = true; + } + + return $this->_opened; + } + + /** + * Closes the connection to the database if it is still open and we were + * the ones that opened it. It is the caller's responsible to close an + * existing connection that was passed to us via $conf['db']. + * + * @return boolean True on success, false on failure. + * @access public + */ + function close() + { + /* If we have a statement object, free it. */ + if (is_object($this->_statement)) { + $this->_statement->free(); + $this->_statement = null; + } + + /* If we opened the database connection, disconnect it. */ + if ($this->_opened && !$this->_existingConnection) { + $this->_opened = false; + return $this->_db->disconnect(); + } + + return ($this->_opened === false); + } + + /** + * Sets this Log instance's identification string. Note that this + * SQL-specific implementation will limit the length of the $ident string + * to sixteen (16) characters. + * + * @param string $ident The new identification string. + * + * @access public + * @since Log 1.8.5 + */ + function setIdent($ident) + { + $this->_ident = substr($ident, 0, $this->_identLimit); + } + + /** + * Inserts $message to the currently open database. Calls open(), + * if necessary. Also passes the message along to any Log_observer + * instances that are observing this Log. + * + * @param mixed $message String or object containing the message to log. + * @param string $priority The priority of the message. Valid + * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, + * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, + * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. + * @return boolean True on success or false on failure. + * @access public + */ + function log($message, $priority = null) + { + /* If a priority hasn't been specified, use the default value. */ + if ($priority === null) { + $priority = $this->_priority; + } + + /* Abort early if the priority is above the maximum logging level. */ + if (!$this->_isMasked($priority)) { + return false; + } + + /* If the connection isn't open and can't be opened, return failure. */ + if (!$this->_opened && !$this->open()) { + return false; + } + + /* If we don't already have a statement object, create one. */ + if (!is_object($this->_statement) && !$this->_prepareStatement()) { + return false; + } + + /* Extract the string representation of the message. */ + $message = $this->_extractMessage($message); + + /* Build our set of values for this log entry. */ + $values = array( + 'id' => $this->_db->nextId($this->_sequence), + 'logtime' => MDB2_Date::mdbNow(), + 'ident' => $this->_ident, + 'priority' => $priority, + 'message' => $message + ); + + /* Execute the SQL query for this log entry insertion. */ + $this->_db->expectError(MDB2_ERROR_NOSUCHTABLE); + $result = &$this->_statement->execute($values); + $this->_db->popExpect(); + + /* Attempt to handle any errors. */ + if (PEAR::isError($result)) { + /* We can only handle MDB2_ERROR_NOSUCHTABLE errors. */ + if ($result->getCode() != MDB2_ERROR_NOSUCHTABLE) { + return false; + } + + /* Attempt to create the target table. */ + if (!$this->_createTable()) { + return false; + } + + /* Recreate our prepared statement resource. */ + $this->_statement->free(); + if (!$this->_prepareStatement()) { + return false; + } + + /* Attempt to re-execute the insertion query. */ + $result = $this->_statement->execute($values); + if (PEAR::isError($result)) { + return false; + } + } + + $this->_announce(array('priority' => $priority, 'message' => $message)); + + return true; + } + + /** + * Create the log table in the database. + * + * @return boolean True on success or false on failure. + * @access private + */ + function _createTable() + { + $this->_db->loadModule('Manager', null, true); + $result = $this->_db->manager->createTable( + $this->_table, + array( + 'id' => array('type' => $this->_types['id']), + 'logtime' => array('type' => $this->_types['logtime']), + 'ident' => array('type' => $this->_types['ident']), + 'priority' => array('type' => $this->_types['priority']), + 'message' => array('type' => $this->_types['message']) + ) + ); + if (PEAR::isError($result)) { + return false; + } + + $result = $this->_db->manager->createIndex( + $this->_table, + 'unique_id', + array('fields' => array('id' => true), 'unique' => true) + ); + if (PEAR::isError($result)) { + return false; + } + + return true; + } + + /** + * Prepare the SQL insertion statement. + * + * @return boolean True if the statement was successfully created. + * + * @access private + * @since Log 1.9.0 + */ + function _prepareStatement() + { + $this->_statement = &$this->_db->prepare( + 'INSERT INTO ' . $this->_table . + ' (id, logtime, ident, priority, message)' . + ' VALUES(:id, :logtime, :ident, :priority, :message)', + $this->_types, MDB2_PREPARE_MANIP); + + /* Return success if we didn't generate an error. */ + return (PEAR::isError($this->_statement) === false); + } +} diff --git a/thirdparty/pear/Log/null.php b/thirdparty/pear/Log/null.php old mode 100644 new mode 100755 index decbca5..5fc69f9 --- a/thirdparty/pear/Log/null.php +++ b/thirdparty/pear/Log/null.php @@ -2,14 +2,14 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 215527 $ * @package Log */ /** * The Log_null class is a concrete implementation of the Log:: abstract * class. It simply consumes log events. - * + * * @author Jon Parise * @since Log 1.8.2 * @package Log @@ -20,7 +20,7 @@ class Log_null extends Log { /** * Constructs a new Log_null object. - * + * * @param string $name Ignored. * @param string $ident The identity string. * @param array $conf The configuration array. @@ -36,9 +36,33 @@ class Log_null extends Log } /** + * Opens the handler. + * + * @access public + * @since Log 1.9.6 + */ + function open() + { + $this->_opened = true; + return true; + } + + /** + * Closes the handler. + * + * @access public + * @since Log 1.9.6 + */ + function close() + { + $this->_opened = false; + return true; + } + + /** * Simply consumes the log event. The message will still be passed * along to any Log_observer instances that are observing this Log. - * + * * @param mixed $message String or object containing the message to log. * @param string $priority The priority of the message. Valid * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, @@ -63,6 +87,5 @@ class Log_null extends Log return true; } -} -?> +} diff --git a/thirdparty/pear/Log/observer.php b/thirdparty/pear/Log/observer.php old mode 100644 new mode 100755 index 08aafe1..43ba04c --- a/thirdparty/pear/Log/observer.php +++ b/thirdparty/pear/Log/observer.php @@ -3,7 +3,7 @@ * $Header$ * $Horde: horde/lib/Log/observer.php,v 1.5 2000/06/28 21:36:13 jon Exp $ * - * @version $Revision$ + * @version $Revision: 211953 $ * @package Log */ @@ -65,29 +65,32 @@ class Log_observer * configuration values. * * @return object The newly created concrete Log_observer - * instance, or an false on an error. + * instance, or null on an error. */ function &factory($type, $priority = PEAR_LOG_INFO, $conf = array()) { $type = strtolower($type); $class = 'Log_observer_' . $type; + /* + * If the desired class already exists (because the caller has supplied + * it from some custom location), simply instantiate and return a new + * instance. + */ + if (class_exists($class)) { + $object = &new $class($priority, $conf); + return $object; + } + /* Support both the new-style and old-style file naming conventions. */ - if (file_exists(dirname(__FILE__) . '/observer_' . $type . '.php')) { - $classfile = 'Log/observer_' . $type . '.php'; - $newstyle = true; - } else { + $newstyle = true; + $classfile = dirname(__FILE__) . '/observer_' . $type . '.php'; + + if (!file_exists($classfile)) { $classfile = 'Log/' . $type . '.php'; $newstyle = false; } - /* Issue a warning if the old-style conventions are being used. */ - if (!$newstyle) - { - trigger_error('Using old-style Log_observer conventions', - E_USER_WARNING); - } - /* * Attempt to include our version of the named class, but don't treat * a failure as fatal. The caller may have already included their own @@ -99,13 +102,15 @@ class Log_observer if (class_exists($class)) { /* Support both new-style and old-style construction. */ if ($newstyle) { - return new $class($priority, $conf); + $object = &new $class($priority, $conf); } else { - return new $class($priority); + $object = &new $class($priority); } + return $object; } - return false; + $null = null; + return $null; } /** @@ -122,5 +127,3 @@ class Log_observer print_r($event); } } - -?> diff --git a/thirdparty/pear/Log/sql.php b/thirdparty/pear/Log/sql.php old mode 100644 new mode 100755 index 4fb5f07..a8efbfb --- a/thirdparty/pear/Log/sql.php +++ b/thirdparty/pear/Log/sql.php @@ -3,12 +3,19 @@ * $Header$ * $Horde: horde/lib/Log/sql.php,v 1.12 2000/08/16 20:27:34 chuck Exp $ * - * @version $Revision$ + * @version $Revision: 250926 $ * @package Log */ -/** PEAR's DB package */ -require_once 'DB.php'; +/** + * We require the PEAR DB class. This is generally defined in the DB.php file, + * but it's possible that the caller may have provided the DB class, or a + * compatible wrapper (such as the one shipped with MDB2), so we first check + * for an existing 'DB' class before including 'DB.php'. + */ +if (!class_exists('DB')) { + require_once 'DB.php'; +} /** * The Log_sql class is a concrete implementation of the Log:: @@ -29,35 +36,57 @@ require_once 'DB.php'; * @author Jon Parise * @since Horde 1.3 * @since Log 1.0 - * @package Log + * @package Log * * @example sql.php Using the SQL handler. */ -class Log_sql extends Log { +class Log_sql extends Log +{ + /** + * Variable containing the DSN information. + * @var mixed + * @access private + */ + var $_dsn = ''; - /** - * Array containing the dsn information. + /** + * String containing the SQL insertion statement. + * * @var string * @access private */ - var $_dsn = ''; + var $_sql = ''; - /** - * Object holding the database handle. + /** + * Array containing our set of DB configuration options. + * @var array + * @access private + */ + var $_options = array('persistent' => true); + + /** + * Object holding the database handle. * @var object * @access private */ var $_db = null; /** + * Resource holding the prepared statement handle. + * @var resource + * @access private + */ + var $_statement = null; + + /** * Flag indicating that we're using an existing database connection. * @var boolean * @access private */ var $_existingConnection = false; - /** - * String holding the database table to use. + /** + * String holding the database table to use. * @var string * @access private */ @@ -86,7 +115,7 @@ class Log_sql extends Log { * @param string $ident The identification field. * @param array $conf The connection configuration array. * @param int $level Log messages up to and including this level. - * @access public + * @access public */ function Log_sql($name, $ident = '', $conf = array(), $level = PEAR_LOG_DEBUG) @@ -95,6 +124,20 @@ class Log_sql extends Log { $this->_table = $name; $this->_mask = Log::UPTO($level); + /* Now that we have a table name, assign our SQL statement. */ + if (!empty($conf['sql'])) { + $this->_sql = $conf['sql']; + } else { + $this->_sql = 'INSERT INTO ' . $this->_table . + ' (id, logtime, ident, priority, message)' . + ' VALUES(?, CURRENT_TIMESTAMP, ?, ?, ?)'; + } + + /* If an options array was provided, use it. */ + if (isset($conf['options']) && is_array($conf['options'])) { + $this->_options = $conf['options']; + } + /* If a specific sequence name was provided, use it. */ if (!empty($conf['sequence'])) { $this->_sequence = $conf['sequence']; @@ -123,15 +166,23 @@ class Log_sql extends Log { * been opened. This is implicitly called by log(), if necessary. * * @return boolean True on success, false on failure. - * @access public + * @access public */ function open() { if (!$this->_opened) { - $this->_db = &DB::connect($this->_dsn, true); + /* Use the DSN and options to create a database connection. */ + $this->_db = &DB::connect($this->_dsn, $this->_options); if (DB::isError($this->_db)) { return false; } + + /* Create a prepared statement for repeated use in log(). */ + if (!$this->_prepareStatement()) { + return false; + } + + /* We now consider out connection open. */ $this->_opened = true; } @@ -144,12 +195,13 @@ class Log_sql extends Log { * existing connection that was passed to us via $conf['db']. * * @return boolean True on success, false on failure. - * @access public + * @access public */ function close() { if ($this->_opened && !$this->_existingConnection) { $this->_opened = false; + $this->_db->freePrepared($this->_statement); return $this->_db->disconnect(); } @@ -182,7 +234,7 @@ class Log_sql extends Log { * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. * @return boolean True on success or false on failure. - * @access public + * @access public */ function log($message, $priority = null) { @@ -201,17 +253,20 @@ class Log_sql extends Log { return false; } + /* If we don't already have our statement object yet, create it. */ + if (!is_object($this->_statement) && !$this->_prepareStatement()) { + return false; + } + /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); - /* Build the SQL query for this log entry insertion. */ + /* Build our set of values for this log entry. */ $id = $this->_db->nextId($this->_sequence); - $q = sprintf('insert into %s (id, logtime, ident, priority, message)' . - 'values(%d, CURRENT_TIMESTAMP, %s, %d, %s)', - $this->_table, $id, $this->_db->quote($this->_ident), - $priority, $this->_db->quote($message)); + $values = array($id, $this->_ident, $priority, $message); - $result = $this->_db->query($q); + /* Execute the SQL query for this log entry insertion. */ + $result =& $this->_db->execute($this->_statement, $values); if (DB::isError($result)) { return false; } @@ -220,6 +275,20 @@ class Log_sql extends Log { return true; } -} -?> + /** + * Prepare the SQL insertion statement. + * + * @return boolean True if the statement was successfully created. + * + * @access private + * @since Log 1.9.1 + */ + function _prepareStatement() + { + $this->_statement = $this->_db->prepare($this->_sql); + + /* Return success if we didn't generate an error. */ + return (DB::isError($this->_statement) === false); + } +} diff --git a/thirdparty/pear/Log/sqlite.php b/thirdparty/pear/Log/sqlite.php old mode 100644 new mode 100755 index 9b7392f..ee50109 --- a/thirdparty/pear/Log/sqlite.php +++ b/thirdparty/pear/Log/sqlite.php @@ -1,22 +1,10 @@ | -// +----------------------------------------------------------------------+ -// -// $Id$ +/** + * $Header$ + * + * @version $Revision: 202069 $ + * @package Log + */ /** * The Log_sqlite class is a concrete implementation of the Log:: @@ -81,7 +69,7 @@ class Log_sqlite extends Log * to open a new database connection * or an already opened sqlite connection. * @param int $level Log messages up to and including this level. - * @access public + * @access public */ function Log_sqlite($name, $ident = '', &$conf, $level = PEAR_LOG_DEBUG) { @@ -106,7 +94,7 @@ class Log_sqlite extends Log * been opened. This is implicitly called by log(), if necessary. * * @return boolean True on success, false on failure. - * @access public + * @access public */ function open() { @@ -139,7 +127,7 @@ class Log_sqlite extends Log * existing connection that was passed to us via $conf['db']. * * @return boolean True on success, false on failure. - * @access public + * @access public */ function close() { @@ -167,7 +155,7 @@ class Log_sqlite extends Log * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING, * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG. * @return boolean True on success or false on failure. - * @access public + * @access public */ function log($message, $priority = null) { @@ -233,6 +221,5 @@ class Log_sqlite extends Log return true; } -} -?> +} diff --git a/thirdparty/pear/Log/syslog.php b/thirdparty/pear/Log/syslog.php old mode 100644 new mode 100755 index ba09472..01be62a --- a/thirdparty/pear/Log/syslog.php +++ b/thirdparty/pear/Log/syslog.php @@ -3,7 +3,7 @@ * $Header$ * $Horde: horde/lib/Log/syslog.php,v 1.6 2000/06/28 21:36:13 jon Exp $ * - * @version $Revision$ + * @version $Revision: 291780 $ * @package Log */ @@ -13,6 +13,7 @@ * (PHP emulates this with the Event Log on Windows machines). * * @author Chuck Hagenbuch + * @author Jon Parise * @since Horde 1.3 * @since Log 1.0 * @package Log @@ -22,13 +23,30 @@ class Log_syslog extends Log { /** - * Integer holding the log facility to use. - * @var string - * @access private - */ + * Integer holding the log facility to use. + * @var integer + * @access private + */ var $_name = LOG_SYSLOG; /** + * Should we inherit the current syslog connection for this process, or + * should we call openlog() to start a new syslog connection? + * @var boolean + * @access private + */ + var $_inherit = false; + + /** + * Maximum message length that will be sent to syslog(). If the handler + * receives a message longer than this length limit, it will be split into + * multiple syslog() calls. + * @var integer + * @access private + */ + var $_maxLength = 500; + + /** * Constructs a new syslog object. * * @param string $name The syslog facility. @@ -45,6 +63,14 @@ class Log_syslog extends Log $name = LOG_SYSLOG; } + if (isset($conf['inherit'])) { + $this->_inherit = $conf['inherit']; + $this->_opened = $this->_inherit; + } + if (isset($conf['maxLength'])) { + $this->_maxLength = $conf['maxLength']; + } + $this->_id = md5(microtime()); $this->_name = $name; $this->_ident = $ident; @@ -59,8 +85,7 @@ class Log_syslog extends Log function open() { if (!$this->_opened) { - openlog($this->_ident, LOG_PID, $this->_name); - $this->_opened = true; + $this->_opened = openlog($this->_ident, LOG_PID, $this->_name); } return $this->_opened; @@ -72,12 +97,12 @@ class Log_syslog extends Log */ function close() { - if ($this->_opened) { + if ($this->_opened && !$this->_inherit) { closelog(); $this->_opened = false; } - return ($this->_opened === false); + return true; } /** @@ -113,10 +138,24 @@ class Log_syslog extends Log /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); - if (!syslog($this->_toSyslog($priority), $message)) { + /* Build a syslog priority value based on our current configuration. */ + $priority = $this->_toSyslog($priority); + if ($this->_inherit) { + $priority |= $this->_name; + } + + /* Split the string into parts based on our maximum length setting. */ + $parts = str_split($message, $this->_maxLength); + if ($parts === false) { return false; } + foreach ($parts as $part) { + if (!syslog($priority, $part)) { + return false; + } + } + $this->_announce(array('priority' => $priority, 'message' => $message)); return true; @@ -156,5 +195,5 @@ class Log_syslog extends Log return $priorities[$priority]; } + } -?> diff --git a/thirdparty/pear/Log/win.php b/thirdparty/pear/Log/win.php old mode 100644 new mode 100755 index 9c59e97..f80d146 --- a/thirdparty/pear/Log/win.php +++ b/thirdparty/pear/Log/win.php @@ -2,7 +2,7 @@ /** * $Header$ * - * @version $Revision$ + * @version $Revision: 278003 $ * @package Log */ @@ -14,7 +14,7 @@ * entitled "JavaScript Power PHP Debugging: * * http://www.zend.com/zend/tut/tutorial-DebugLib.php - * + * * @author Jon Parise * @since Log 1.7.0 * @package Log @@ -38,19 +38,19 @@ class Log_win extends Log var $_title = 'Log Output Window'; /** - * Mapping of log priorities to colors. + * Mapping of log priorities to styles. * @var array * @access private */ - var $_colors = array( - PEAR_LOG_EMERG => 'red', - PEAR_LOG_ALERT => 'orange', - PEAR_LOG_CRIT => 'yellow', - PEAR_LOG_ERR => 'green', - PEAR_LOG_WARNING => 'blue', - PEAR_LOG_NOTICE => 'indigo', - PEAR_LOG_INFO => 'violet', - PEAR_LOG_DEBUG => 'black' + var $_styles = array( + PEAR_LOG_EMERG => 'color: red;', + PEAR_LOG_ALERT => 'color: orange;', + PEAR_LOG_CRIT => 'color: yellow;', + PEAR_LOG_ERR => 'color: green;', + PEAR_LOG_WARNING => 'color: blue;', + PEAR_LOG_NOTICE => 'color: indigo;', + PEAR_LOG_INFO => 'color: violet;', + PEAR_LOG_DEBUG => 'color: black;' ); /** @@ -62,7 +62,7 @@ class Log_win extends Log /** * Constructs a new Log_win object. - * + * * @param string $name Ignored. * @param string $ident The identity string. * @param array $conf The configuration array. @@ -73,15 +73,20 @@ class Log_win extends Log $level = PEAR_LOG_DEBUG) { $this->_id = md5(microtime()); - $this->_name = $name; + $this->_name = str_replace(' ', '_', $name); $this->_ident = $ident; $this->_mask = Log::UPTO($level); if (isset($conf['title'])) { $this->_title = $conf['title']; } + if (isset($conf['styles']) && is_array($conf['styles'])) { + $this->_styles = $conf['styles']; + } if (isset($conf['colors']) && is_array($conf['colors'])) { - $this->_colors = $conf['colors']; + foreach ($conf['colors'] as $level => $color) { + $this->_styles[$level] .= "color: $color;"; + } } register_shutdown_function(array(&$this, '_Log_win')); @@ -109,6 +114,7 @@ class Log_win extends Log { if (!$this->_opened) { $win = $this->_name; + $styles = $this->_styles; if (!empty($this->_ident)) { $identHeader = "$win.document.writeln('Ident')"; @@ -116,7 +122,7 @@ class Log_win extends Log $identHeader = ''; } - echo <<< END_OF_SCRIPT + echo <<< EOT -END_OF_SCRIPT; +EOT; $this->_opened = true; } @@ -162,6 +183,7 @@ END_OF_SCRIPT; if ($this->_opened) { $this->_writeln(''); $this->_writeln(''); + $this->_drainBuffer(); $this->_opened = false; } @@ -169,7 +191,26 @@ END_OF_SCRIPT; } /** - * Writes a single line of text to the output window. + * Writes the contents of the output buffer to the output window. + * + * @access private + */ + function _drainBuffer() + { + $win = $this->_name; + foreach ($this->_buffer as $line) { + echo "\n"; + } + + /* Now that the buffer has been drained, clear it. */ + $this->_buffer = array(); + } + + /** + * Writes a single line of text to the output buffer. * * @param string $line The line of text to write. * @@ -187,26 +228,17 @@ END_OF_SCRIPT; /* If we haven't already opened the output window, do so now. */ if (!$this->_opened && !$this->open()) { - return false; + return; } /* Drain the buffer to the output window. */ - $win = $this->_name; - foreach ($this->_buffer as $line) { - echo "\n"; - } - - /* Now that the buffer has been drained, clear it. */ - $this->_buffer = array(); + $this->_drainBuffer(); } /** * Logs $message to the output window. The message is also passed along * to any Log_observer instances that are observing this Log. - * + * * @param mixed $message String or object containing the message to log. * @param string $priority The priority of the message. Valid * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT, @@ -229,20 +261,19 @@ END_OF_SCRIPT; /* Extract the string representation of the message. */ $message = $this->_extractMessage($message); + $message = preg_replace('/\r\n|\n|\r/', '
    ', $message); list($usec, $sec) = explode(' ', microtime()); /* Build the output line that contains the log entry row. */ - $line = ''; + $line = ''; $line .= sprintf('%s.%s', - strftime('%T', $sec), substr($usec, 2, 2)); + strftime('%H:%M:%S', $sec), substr($usec, 2, 2)); if (!empty($this->_ident)) { $line .= '' . $this->_ident . ''; } $line .= '' . ucfirst($this->priorityToString($priority)) . ''; - $line .= sprintf('%s', - $this->_colors[$priority], - preg_replace('/\r\n|\n|\r/', '
    ', $message)); + $line .= sprintf('%s', $priority, $message); $line .= ''; $this->_writeln($line); @@ -251,6 +282,5 @@ END_OF_SCRIPT; return true; } -} -?> +} diff --git a/thirdparty/pear/MIME/Type.php b/thirdparty/pear/MIME/Type.php old mode 100644 new mode 100755 index 6acc15e..79ab79d --- a/thirdparty/pear/MIME/Type.php +++ b/thirdparty/pear/MIME/Type.php @@ -3,7 +3,7 @@ // +----------------------------------------------------------------------+ // | PHP version 4 | // +----------------------------------------------------------------------+ -// | Copyright (c) 1997-2002 The PHP Group | +// | Copyright (c) 1997-2002, 2008 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 3.0 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | @@ -16,7 +16,7 @@ // | Authors: Ian Eure | // +----------------------------------------------------------------------+ // -// $Id: Type.php,v 1.2 2004/08/07 22:19:04 ieure Exp $ +// $Id: Type.php,v 1.6 2009/01/16 11:49:45 cweiske Exp $ require_once 'PEAR.php'; @@ -26,34 +26,40 @@ $_fileCmd = 'file'; /** * Class for working with MIME types * - * @version @version@ - * @package @package@ - * @author Ian Eure + * @category MIME + * @package MIME_Type + * @license PHP License 3.0 + * @version 1.2.0 + * @link http://pear.php.net/package/MIME_Type + * @author Ian Eure */ -class MIME_Type { +class MIME_Type +{ /** * The MIME media type * * @var string */ var $media = ''; - + /** * The MIME media sub-type * * @var string */ var $subType = ''; - + /** * Optional MIME parameters * * @var array */ var $parameters = array(); - + /** - * List of valid media types + * List of valid media types. + * A media type is the string in front of the slash. + * The media type of "text/xml" would be "text". * * @var array */ @@ -71,11 +77,12 @@ class MIME_Type { /** * Constructor. * - * If $type is set, if will be parsed and the appropriate class vars set. If not, - * you get an empty class. This is useful, but not quite as useful as parsing a - * type. + * If $type is set, if will be parsed and the appropriate class vars set. + * If not, you get an empty class. + * This is useful, but not quite as useful as parsing a type. + * + * @param string $type MIME type * - * @param string $type MIME type * @return void */ function MIME_Type($type = false) @@ -87,19 +94,21 @@ class MIME_Type { /** - * Parse a mime-type + * Parse a mime-type and set the class variables. + * + * @param string $type MIME type to parse * - * @param $type string MIME type to parse * @return void */ function parse($type) { - $this->media = $this->getMedia($type); - $this->subType = $this->getSubType($type); + $this->media = $this->getMedia($type); + $this->subType = $this->getSubType($type); + $this->parameters = array(); if (MIME_Type::hasParameters($type)) { require_once 'MIME/Type/Parameter.php'; foreach (MIME_Type::getParameters($type) as $param) { - $param = &new MIME_Type_Parameter($param); + $param = new MIME_Type_Parameter($param); $this->parameters[$param->name] = $param; } } @@ -109,7 +118,8 @@ class MIME_Type { /** * Does this type have any parameters? * - * @param $type string MIME type to check + * @param string $type MIME type to check + * * @return boolean true if $type has parameters, false otherwise * @static */ @@ -125,25 +135,27 @@ class MIME_Type { /** * Get a MIME type's parameters * - * @param $type string MIME type to get parameters of + * @param string $type MIME type to get parameters of + * * @return array $type's parameters * @static */ function getParameters($type) { $params = array(); - $tmp = explode(';', $type); + $tmp = explode(';', $type); for ($i = 1; $i < count($tmp); $i++) { $params[] = trim($tmp[$i]); } return $params; } - + /** - * Strip paramaters from a MIME type string + * Strip parameters from a MIME type string. + * + * @param string $type MIME type string * - * @param string $type MIME type string * @return string MIME type with parameters removed * @static */ @@ -157,32 +169,96 @@ class MIME_Type { /** + * Removes comments from a media type, subtype or parameter. + * + * @param string $string String to strip comments from + * @param string &$comment Comment is stored in there. + * + * @return string String without comments + * @static + */ + function stripComments($string, &$comment) + { + if (strpos($string, '(') === false) { + return $string; + } + + $inquote = false; + $quoting = false; + $incomment = 0; + $newstring = ''; + + for ($n = 0; $n < strlen($string); $n++) { + if ($quoting) { + if ($incomment == 0) { + $newstring .= $string[$n]; + } else if ($comment !== null) { + $comment .= $string[$n]; + } + $quoting = false; + } else if ($string[$n] == '\\') { + $quoting = true; + } else if (!$inquote && $incomment > 0 && $string[$n] == ')') { + $incomment--; + if ($incomment == 0 && $comment !== null) { + $comment .= ' '; + } + } else if (!$inquote && $string[$n] == '(') { + $incomment++; + } else if ($string[$n] == '"') { + if ($inquote) { + $inquote = false; + } else { + $inquote = true; + } + } else if ($incomment == 0) { + $newstring .= $string[$n]; + } else if ($comment !== null) { + $comment .= $string[$n]; + } + } + + if ($comment !== null) { + $comment = trim($comment); + } + + return $newstring; + } + + + /** * Get a MIME type's media * - * @note 'media' refers to the portion before the first slash - * @param $type string MIME type to get media of + * @note 'media' refers to the portion before the first slash + * + * @param string $type MIME type to get media of + * * @return string $type's media * @static */ function getMedia($type) { $tmp = explode('/', $type); - return strtolower($tmp[0]); + return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); } /** * Get a MIME type's subtype * - * @param $type string MIME type to get subtype of - * @return string $type's subtype + * @param string $type MIME type to get subtype of + * + * @return string $type's subtype, null if invalid mime type * @static */ function getSubType($type) { $tmp = explode('/', $type); + if (!isset($tmp[1])) { + return null; + } $tmp = explode(';', $tmp[1]); - return strtolower(trim($tmp[0])); + return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); } @@ -195,10 +271,10 @@ class MIME_Type { */ function get() { - $type = strtolower($this->media.'/'.$this->subType); + $type = strtolower($this->media . '/' . $this->subType); if (count($this->parameters)) { foreach ($this->parameters as $key => $null) { - $type .= '; '.$this->parameters[$key]->get(); + $type .= '; ' . $this->parameters[$key]->get(); } } return $type; @@ -208,9 +284,11 @@ class MIME_Type { /** * Is this type experimental? * - * @note Experimental types are denoted by a leading 'x-' in the media or - * subtype, e.g. text/x-vcard or x-world/x-vrml. - * @param string $type MIME type to check + * @note Experimental types are denoted by a leading 'x-' in the media or + * subtype, e.g. text/x-vcard or x-world/x-vrml. + * + * @param string $type MIME type to check + * * @return boolean true if $type is experimental, false otherwise * @static */ @@ -227,8 +305,10 @@ class MIME_Type { /** * Is this a vendor MIME type? * - * @note Vendor types are denoted with a leading 'vnd. in the subtype. - * @param string $type MIME type to check + * @note Vendor types are denoted with a leading 'vnd. in the subtype. + * + * @param string $type MIME type to check + * * @return boolean true if $type is a vendor type, false otherwise * @static */ @@ -244,7 +324,8 @@ class MIME_Type { /** * Is this a wildcard type? * - * @param string $type MIME type to check + * @param string $type MIME type to check + * * @return boolean true if $type is a wildcard, false otherwise * @static */ @@ -263,24 +344,26 @@ class MIME_Type { * Example: * MIME_Type::wildcardMatch('image/*', 'image/png') * - * @param string $card Wildcard to check against - * @param string $type MIME type to check + * @param string $card Wildcard to check against + * @param string $type MIME type to check + * * @return boolean true if there was a match, false otherwise + * @static */ function wildcardMatch($card, $type) { if (!MIME_Type::isWildcard($card)) { return false; } - + if ($card == '*/*') { return true; } - - if (MIME_Type::getMedia($card) == - MIME_Type::getMedia($type)) { + + if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) { return true; } + return false; } @@ -288,17 +371,19 @@ class MIME_Type { /** * Add a parameter to this type * - * @param string $name Attribute name - * @param string $value Attribute value - * @param string $comment Comment for this parameter + * @param string $name Attribute name + * @param string $value Attribute value + * @param string $comment Comment for this parameter + * * @return void */ function addParameter($name, $value, $comment = false) { - $tmp = &new MIME_Type_Parameter; - $tmp->name = $name; - $tmp->value = $value; - $tmp->comment = $comment; + $tmp = new MIME_Type_Parameter(); + + $tmp->name = $name; + $tmp->value = $value; + $tmp->comment = $comment; $this->parameters[$name] = $tmp; } @@ -306,12 +391,13 @@ class MIME_Type { /** * Remove a parameter from this type * - * @param string $name Parameter name + * @param string $name Parameter name + * * @return void */ function removeParameter($name) { - unset ($this->parameters[$name]); + unset($this->parameters[$name]); } @@ -320,23 +406,71 @@ class MIME_Type { * * This function may be called staticly. * - * @param string $file Path to the file to get the type of - * @param bool $params Append MIME parameters if true + * @internal Tries to use fileinfo extension at first. If that + * does not work, mime_magic is used. If this is also not available + * or does not succeed, "file" command is tried to be executed with + * System_Command. When that fails, too, then we use our in-built + * extension-to-mimetype-mapping list. + * + * @param string $file Path to the file to get the type of + * @param bool $params Append MIME parameters if true + * * @return string $file's MIME-type on success, PEAR_Error otherwise + * * @since 1.0.0beta1 * @static */ function autoDetect($file, $params = false) { - @include_once 'System/Command.php'; + // Sanity checks + if (!file_exists($file)) { + return PEAR::raiseError("File \"$file\" doesn't exist"); + } + + if (!is_readable($file)) { + return PEAR::raiseError("File \"$file\" is not readable"); + } + + if (function_exists('finfo_file')) { + $finfo = finfo_open(FILEINFO_MIME); + $type = finfo_file($finfo, $file); + finfo_close($finfo); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } + } + if (function_exists('mime_content_type')) { $type = mime_content_type($file); - } else if (class_exists('System_Command')) { - $type = MIME_Type::_fileAutoDetect($file); - } else { - return PEAR::raiseError("Sorry, can't autodetect; you need the mime_magic extension or System_Command and 'file' installed to use this function."); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } } + @include_once 'System/Command.php'; + if (class_exists('System_Command')) { + return MIME_Type::_handleDetection( + MIME_Type::_fileAutoDetect($file), + $params + ); + } + + require_once 'MIME/Type/Extension.php'; + $mte = new MIME_Type_Extension(); + return $mte->getMIMEType($file); + } + + + /** + * Handles a detected MIME type and modifies it if necessary. + * + * @param string $type MIME Type of a file + * @param bool $params Append MIME parameters if true + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + */ + function _handleDetection($type, $params) + { // _fileAutoDetect() may have returned an error. if (PEAR::isError($type)) { return $type; @@ -355,29 +489,22 @@ class MIME_Type { return $type; } + /** * Autodetect a file's MIME-type with 'file' and System_Command * * This function may be called staticly. * - * @param string $file Path to the file to get the type of + * @param string $file Path to the file to get the type of + * * @return string $file's MIME-type + * * @since 1.0.0beta1 * @static */ function _fileAutoDetect($file) { - // Sanity checks - if (!file_exists($file)) { - return PEAR::raiseError("File \"$file\" doesn't exist"); - } - - if (!is_readable($file)) { - return PEAR::raiseError("File \"$file\" is not readable"); - } - - $cmd = new System_Command; - + $cmd = new System_Command(); // Make sure we have the 'file' command. $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd'); @@ -386,10 +513,11 @@ class MIME_Type { return PEAR::raiseError("Can't find file command \"{$fileCmd}\""); } - $cmd->pushCommand($fileCmd, "-bi '{$file}'"); + $cmd->pushCommand($fileCmd, "-bi " . escapeshellarg($file)); $res = $cmd->execute(); unset($cmd); return $res; } + } \ No newline at end of file diff --git a/thirdparty/pear/MIME/Type/Extension.php b/thirdparty/pear/MIME/Type/Extension.php new file mode 100644 index 0000000..1987e2a --- /dev/null +++ b/thirdparty/pear/MIME/Type/Extension.php @@ -0,0 +1,298 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Extension.php,v 1.1 2009/01/16 11:49:45 cweiske Exp $ + +require_once 'PEAR.php'; + +/** + * Class for mapping file extensions to MIME types. + * + * @category MIME + * @package MIME_Type + * @author Christian Schmidt + * @license PHP License 3.0 + * @version 1.2.0 + * @link http://pear.php.net/package/MIME_Type + */ +class MIME_Type_Extension +{ + /** + * Mapping between file extension and MIME type. + * + * @internal The array is sorted alphabetically by value and with primary + * extension first. Be careful about not adding duplicate keys - PHP + * silently ignores duplicates. The following command can be used for + * checking for duplicates: + * grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d + * application/octet-stream is generally used as fallback when no other + * MIME-type can be found, but the array does not contain a lot of such + * unknown extension. One entry exists, though, to allow detection of + * file extension for this MIME-type. + * + * @var array + */ + var $extensionToType = array ( + 'ez' => 'application/andrew-inset', + 'atom' => 'application/atom+xml', + 'jar' => 'application/java-archive', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'mathml' => 'application/mathml+xml', + 'doc' => 'application/msword', + 'dat' => 'application/octet-stream', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'rdf' => 'application/rdf+xml', + 'rss' => 'application/rss+xml', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'mif' => 'application/vnd.mif', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xls' => 'application/vnd.ms-excel', + 'xlb' => 'application/vnd.ms-excel', + 'xlt' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', + 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', + 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', + 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pps' => 'application/vnd.ms-powerpoint', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'vsd' => 'application/vnd.visio', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'vxml' => 'application/voicexml+xml', + 'bcpio' => 'application/x-bcpio', + 'vcd' => 'application/x-cdlink', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'application/x-csh', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'spl' => 'application/x-futuresplash', + 'tgz' => 'application/x-gtar', + 'gtar' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'js' => 'application/x-javascript', + 'skp' => 'application/x-koan', + 'skd' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'latex' => 'application/x-latex', + 'nc' => 'application/x-netcdf', + 'cdf' => 'application/x-netcdf', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texinfo' => 'application/x-texinfo', + 'texi' => 'application/x-texinfo', + 't' => 'application/x-troff', + 'tr' => 'application/x-troff', + 'roff' => 'application/x-troff', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'ms' => 'application/x-troff-ms', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'xslt' => 'application/xslt+xml', + 'xml' => 'application/xml', + 'xsl' => 'application/xml', + 'dtd' => 'application/xml-dtd', + 'zip' => 'application/zip', + 'au' => 'audio/basic', + 'snd' => 'audio/basic', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'kar' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'm3u' => 'audio/x-mpegurl', + 'wma' => 'audio/x-ms-wma', + 'wax' => 'audio/x-ms-wax', + 'ram' => 'audio/x-pn-realaudio', + 'ra' => 'audio/x-pn-realaudio', + 'rm' => 'application/vnd.rn-realmedia', + 'wav' => 'audio/x-wav', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/bmp', + 'cgm' => 'image/cgm', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'djvu' => 'image/vnd.djvu', + 'djv' => 'image/vnd.djvu', + 'wbmp' => 'image/vnd.wap.wbmp', + 'ras' => 'image/x-cmu-raster', + 'ico' => 'image/x-icon', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'psd' => 'image/x-photoshop', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'eml' => 'message/rfc822', + 'igs' => 'model/iges', + 'iges' => 'model/iges', + 'msh' => 'model/mesh', + 'mesh' => 'model/mesh', + 'silo' => 'model/mesh', + 'wrl' => 'model/vrml', + 'vrml' => 'model/vrml', + 'ics' => 'text/calendar', + 'ifb' => 'text/calendar', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'html' => 'text/html', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'asc' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'sgml' => 'text/sgml', + 'sgm' => 'text/sgml', + 'tsv' => 'text/tab-separated-values', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 'etx' => 'text/x-setext', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mxu' => 'video/vnd.mpegurl', + 'm4u' => 'video/vnd.mpegurl', + 'flv' => 'video/x-flv', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'wmv' => 'video/x-ms-wmv', + 'wm' => 'video/x-ms-wm', + 'wmx' => 'video/x-ms-wmx', + 'avi' => 'video/x-msvideo', + 'ogv' => 'video/ogg', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + ); + + + + /** + * Autodetect a file's MIME-type. + * + * @param string $file Path to the file to get the type of + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + */ + function getMIMEType($file) + { + $extension = substr(strrchr($file, '.'), 1); + if ($extension === false) { + return PEAR::raiseError("File has no extension."); + } + + if (!isset($this->extensionToType[$extension])) { + return PEAR::raiseError("Sorry, couldn't determine file type."); + } + + return $this->extensionToType[$extension]; + } + + + + /** + * Return default MIME-type for the specified extension. + * + * @param string $type MIME-type + * + * @return string A file extension without leading period. + */ + function getExtension($type) + { + require_once 'MIME/Type.php'; + // Strip parameters and comments. + $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type); + + $extension = array_search($type, $this->extensionToType); + if ($extension === false) { + return PEAR::raiseError("Sorry, couldn't determine extension."); + } + return $extension; + } + +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/MIME/Type/Parameter.php b/thirdparty/pear/MIME/Type/Parameter.php old mode 100644 new mode 100755 index c37d7bb..399d3dd --- a/thirdparty/pear/MIME/Type/Parameter.php +++ b/thirdparty/pear/MIME/Type/Parameter.php @@ -16,13 +16,13 @@ // | Authors: Ian Eure | // +----------------------------------------------------------------------+ // -// $Id: Parameter.php,v 1.1 2004/06/16 08:46:19 ieure Exp $ +// $Id: Parameter.php,v 1.1 2007/03/25 10:10:21 cweiske Exp $ /** * Class for working with MIME type parameters * - * @version @version@ - * @package @package@ + * @version 1.2.0 + * @package MIME_Type * @author Ian Eure */ class MIME_Type_Parameter { @@ -32,14 +32,14 @@ class MIME_Type_Parameter { * @var string */ var $name; - + /** * Parameter value * * @var string */ var $value; - + /** * Parameter comment * @@ -61,7 +61,7 @@ class MIME_Type_Parameter { } } - + /** * Parse a MIME type parameter and set object fields * @@ -70,11 +70,11 @@ class MIME_Type_Parameter { */ function parse($param) { - $this->name = $this->getAttribute($param); - $this->value = $this->getValue($param); - if ($this->hasComment($param)) { - $this->comment = $this->getComment($param); - } + $comment = ''; + $param = MIME_Type::stripComments($param, $comment); + $this->name = $this->getAttribute($param); + $this->value = $this->getValue($param); + $this->comment = $comment; } @@ -101,13 +101,14 @@ class MIME_Type_Parameter { */ function getValue($param) { - $tmp = explode('=', $param); + $tmp = explode('=', $param, 2); $value = $tmp[1]; - if (MIME_Type_Parameter::hasComment($param)) { - $cs = strpos($value, '('); - $value = substr($value, 0, $cs); + $value = trim($value); + if ($value[0] == '"' && $value[strlen($value)-1] == '"') { + $value = substr($value, 1, -1); } - return trim($value, '" '); + $value = str_replace('\\"', '"', $value); + return $value; } @@ -152,9 +153,9 @@ class MIME_Type_Parameter { */ function get() { - $val = $this->name.'="'.$this->value.'"'; + $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"'; if ($this->comment) { - $val .= ' ('.$this->comment.')'; + $val .= ' (' . $this->comment . ')'; } return $val; } diff --git a/thirdparty/pear/Net/CheckIP.php b/thirdparty/pear/Net/CheckIP.php old mode 100644 new mode 100755 index 99e348b..99e348b --- a/thirdparty/pear/Net/CheckIP.php +++ b/thirdparty/pear/Net/CheckIP.php diff --git a/thirdparty/pear/Net/Curl.php b/thirdparty/pear/Net/Curl.php old mode 100644 new mode 100755 index 2aa0240..2aa0240 --- a/thirdparty/pear/Net/Curl.php +++ b/thirdparty/pear/Net/Curl.php diff --git a/thirdparty/pear/Net/DIME.php b/thirdparty/pear/Net/DIME.php old mode 100644 new mode 100755 index fc69af5..eddbaa9 --- a/thirdparty/pear/Net/DIME.php +++ b/thirdparty/pear/Net/DIME.php @@ -1,60 +1,59 @@ | -// | Ralf Hofmann | -// +----------------------------------------------------------------------+ -// -// $Id$ -// - -require_once 'PEAR.php'; /** + * This file holds the Net_DIME_Message and Net_DIME_Record classes and all + * constants defined for the Net_DIME package. * - * DIME Encoding/Decoding - * - * What is it? - * This class enables you to manipulate and build - * a DIME encapsulated message. + * PHP versions 4 and 5 * - * http://www.ietf.org/internet-drafts/draft-nielsen-dime-02.txt - * - * 09/18/02 Ralf - A huge number of changes to be compliant - * with the DIME Specification Release 17 June 2002 + * Copyright (c) 2002-2007 The PHP Group + * All rights reserved. * - * TODO: lots of stuff needs to be tested. - * Definitily have to go through DIME spec and - * make things work right, most importantly, sec 3.3 - * make examples, document - * - * see test/dime_mesage_test.php for example of usage + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. * - * @author Shane Caraveo , - * Ralf Hofmann - * @version $Revision$ - * @package Net_DIME + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @category Networking + * @package Net_DIME + * @author Shane Caraveo + * @author Ralf Hofmann + * @author Jan Schneider + * @copyright 2002-2007 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://pear.php.net/package/Net_DIME + * @example test/dime_record_test.php For example of usage. */ -define('NET_DIME_TYPE_UNCHANGED',0x00); -define('NET_DIME_TYPE_MEDIA',0x01); -define('NET_DIME_TYPE_URI',0x02); -define('NET_DIME_TYPE_UNKNOWN',0x03); -define('NET_DIME_TYPE_NONE',0x04); -define('NET_DIME_VERSION',0x0001); +/** PEAR */ +require_once 'PEAR.php'; + +define('NET_DIME_TYPE_UNCHANGED', 0x00); +define('NET_DIME_TYPE_MEDIA', 0x01); +define('NET_DIME_TYPE_URI', 0x02); +define('NET_DIME_TYPE_UNKNOWN', 0x03); +define('NET_DIME_TYPE_NONE', 0x04); -define('NET_DIME_RECORD_HEADER',12); +define('NET_DIME_VERSION', 0x0001); + +define('NET_DIME_RECORD_HEADER', 12); define('NET_DIME_FLAGS', 0); define('NET_DIME_OPTS_LEN', 1); @@ -66,19 +65,34 @@ define('NET_DIME_ID', 6); define('NET_DIME_TYPE', 7); define('NET_DIME_DATA', 8); -class Net_DIME_Record extends PEAR -{ +/** + * Net_DIME_Record encodes and decodes single DIME records. + * + * @category Networking + * @package Net_DIME + * @author Shane Caraveo + * @author Ralf Hofmann + * @author Jan Schneider + * @copyright 2002-2007 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @see Net_DIME_Message + * @link http://pear.php.net/package/Net_DIME + * @link http://www.ietf.org/internet-drafts/draft-nielsen-dime-02.txt + */ +class Net_DIME_Record { + // these are used to hold the padded length var $OPTS_LENGTH = 0; var $ID_LENGTH = 0; - var $TYPE_LENGTH = 0; + var $TYPE_LENGTH = 0; var $DATA_LENGTH = 0; - var $_haveOpts = FALSE; - var $_haveID = FALSE; - var $_haveType = FALSE; - var $_haveData = FALSE; - var $debug = FALSE; + var $_haveOpts = false; + var $_haveID = false; + var $_haveType = false; + var $_haveData = false; + var $debug = false; var $padstr = "\0"; + /** * Elements * [NET_DIME_FLAGS], 16 bits: VERSION:MB:ME:CF:TYPE_T @@ -91,18 +105,20 @@ class Net_DIME_Record extends PEAR * [NET_DIME_TYPE] : TYPE * [NET_DIME_DATA] : DATA */ - var $Elements = array(NET_DIME_FLAGS => 0, NET_DIME_OPTS_LEN => 0, - NET_DIME_ID_LEN => 0, NET_DIME_TYPE_LEN => 0, + var $Elements = array(NET_DIME_FLAGS => 0, NET_DIME_OPTS_LEN => 0, + NET_DIME_ID_LEN => 0, NET_DIME_TYPE_LEN => 0, NET_DIME_DATA_LEN => 0, NET_DIME_OPTS => '', NET_DIME_ID => '', NET_DIME_TYPE => '', NET_DIME_DATA => ''); - - function Net_DIME_Record($debug = FALSE) + + function Net_DIME_Record($debug = false) { $this->debug = $debug; - if ($debug) $this->padstr = '*'; + if ($debug) { + $this->padstr = '*'; + } } function setMB() @@ -129,12 +145,12 @@ class Net_DIME_Record extends PEAR { return $this->Elements[NET_DIME_FLAGS] & 0x0200; } - + function isStart() { return $this->Elements[NET_DIME_FLAGS] & 0x0400; } - + function getID() { return $this->Elements[NET_DIME_ID]; @@ -149,13 +165,13 @@ class Net_DIME_Record extends PEAR { return $this->Elements[NET_DIME_DATA]; } - + function getDataLength() { return $this->Elements[NET_DIME_DATA_LEN]; } - - function setType($typestring, $type=NET_DIME_TYPE_UNKNOWN) + + function setType($typestring, $type = NET_DIME_TYPE_UNKNOWN) { $typelen = strlen($typestring) & 0xFFFF; $type = $type << 4; @@ -164,14 +180,14 @@ class Net_DIME_Record extends PEAR $this->TYPE_LENGTH = $this->_getPadLength($typelen); $this->Elements[NET_DIME_TYPE] = $typestring; } - + function generateID() { $id = md5(time()); $this->setID($id); return $id; } - + function setID($id) { $idlen = strlen($id) & 0xFFFF; @@ -179,45 +195,46 @@ class Net_DIME_Record extends PEAR $this->ID_LENGTH = $this->_getPadLength($idlen); $this->Elements[NET_DIME_ID] = $id; } - - function setData($data, $size=0) + + function setData($data, $size = 0) { - $datalen = $size?$size:strlen($data); + $datalen = $size ? $size : strlen($data); $this->Elements[NET_DIME_DATA_LEN] = $datalen; $this->DATA_LENGTH = $this->_getPadLength($datalen); $this->Elements[NET_DIME_DATA] = $data; } - + function encode() { - // insert version + // Insert version. $this->Elements[NET_DIME_FLAGS] = ($this->Elements[NET_DIME_FLAGS] & 0x07FF) | (NET_DIME_VERSION << 11); - // the real dime encoding - $format = '%c%c%c%c%c%c%c%c%c%c%c%c'. - '%'.$this->OPTS_LENGTH.'s'. - '%'.$this->ID_LENGTH.'s'. - '%'.$this->TYPE_LENGTH.'s'. - '%'.$this->DATA_LENGTH.'s'; + // The real DIME encoding. + $format = '%c%c%c%c%c%c%c%c%c%c%c%c' + . '%' . $this->OPTS_LENGTH . 's' + . '%' . $this->ID_LENGTH . 's' + . '%' . $this->TYPE_LENGTH . 's' + . '%' . $this->DATA_LENGTH . 's'; + return sprintf($format, - ($this->Elements[NET_DIME_FLAGS]&0x0000FF00)>>8, - ($this->Elements[NET_DIME_FLAGS]&0x000000FF), - ($this->Elements[NET_DIME_OPTS_LEN]&0x0000FF00)>>8, - ($this->Elements[NET_DIME_OPTS_LEN]&0x000000FF), - ($this->Elements[NET_DIME_ID_LEN]&0x0000FF00)>>8, - ($this->Elements[NET_DIME_ID_LEN]&0x000000FF), - ($this->Elements[NET_DIME_TYPE_LEN]&0x0000FF00)>>8, - ($this->Elements[NET_DIME_TYPE_LEN]&0x000000FF), - ($this->Elements[NET_DIME_DATA_LEN]&0xFF000000)>>24, - ($this->Elements[NET_DIME_DATA_LEN]&0x00FF0000)>>16, - ($this->Elements[NET_DIME_DATA_LEN]&0x0000FF00)>>8, - ($this->Elements[NET_DIME_DATA_LEN]&0x000000FF), + ($this->Elements[NET_DIME_FLAGS] & 0x0000FF00) >> 8, + ($this->Elements[NET_DIME_FLAGS] & 0x000000FF), + ($this->Elements[NET_DIME_OPTS_LEN] & 0x0000FF00) >> 8, + ($this->Elements[NET_DIME_OPTS_LEN] & 0x000000FF), + ($this->Elements[NET_DIME_ID_LEN] & 0x0000FF00) >> 8, + ($this->Elements[NET_DIME_ID_LEN] & 0x000000FF), + ($this->Elements[NET_DIME_TYPE_LEN] & 0x0000FF00) >> 8, + ($this->Elements[NET_DIME_TYPE_LEN] & 0x000000FF), + ($this->Elements[NET_DIME_DATA_LEN] & 0xFF000000) >> 24, + ($this->Elements[NET_DIME_DATA_LEN] & 0x00FF0000) >> 16, + ($this->Elements[NET_DIME_DATA_LEN] & 0x0000FF00) >> 8, + ($this->Elements[NET_DIME_DATA_LEN] & 0x000000FF), str_pad($this->Elements[NET_DIME_OPTS], $this->OPTS_LENGTH, $this->padstr), str_pad($this->Elements[NET_DIME_ID], $this->ID_LENGTH, $this->padstr), str_pad($this->Elements[NET_DIME_TYPE], $this->TYPE_LENGTH, $this->padstr), str_pad($this->Elements[NET_DIME_DATA], $this->DATA_LENGTH, $this->padstr)); } - + function _getPadLength($len) { $pad = 0; @@ -227,43 +244,46 @@ class Net_DIME_Record extends PEAR } return $len + $pad; } - - function decode(&$data) - { - // REAL DIME decoding - $this->Elements[NET_DIME_FLAGS] = (hexdec(bin2hex($data[0]))<<8) + hexdec(bin2hex($data[1])); - $this->Elements[NET_DIME_OPTS_LEN] = (hexdec(bin2hex($data[2]))<<8) + hexdec(bin2hex($data[3])); - $this->Elements[NET_DIME_ID_LEN] = (hexdec(bin2hex($data[4]))<<8) + hexdec(bin2hex($data[5])); - $this->Elements[NET_DIME_TYPE_LEN] = (hexdec(bin2hex($data[6]))<<8) + hexdec(bin2hex($data[7])); - $this->Elements[NET_DIME_DATA_LEN] = (hexdec(bin2hex($data[8]))<<24) + - (hexdec(bin2hex($data[9]))<<16) + - (hexdec(bin2hex($data[10]))<<8) + - hexdec(bin2hex($data[11])); + + function decode($data) + { + // Real DIME decoding. + $this->Elements[NET_DIME_FLAGS] = (hexdec(bin2hex($data[0])) << 8) + + hexdec(bin2hex($data[1])); + $this->Elements[NET_DIME_OPTS_LEN] = (hexdec(bin2hex($data[2])) << 8) + + hexdec(bin2hex($data[3])); + $this->Elements[NET_DIME_ID_LEN] = (hexdec(bin2hex($data[4])) << 8) + + hexdec(bin2hex($data[5])); + $this->Elements[NET_DIME_TYPE_LEN] = (hexdec(bin2hex($data[6])) << 8) + + hexdec(bin2hex($data[7])); + $this->Elements[NET_DIME_DATA_LEN] = (hexdec(bin2hex($data[8])) << 24) + + (hexdec(bin2hex($data[9])) << 16) + + (hexdec(bin2hex($data[10])) << 8) + + hexdec(bin2hex($data[11])); $p = 12; - - $version = (($this->Elements[NET_DIME_FLAGS]>>11) & 0x001F); - - if ($version == NET_DIME_VERSION) - { - $this->OPTS_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_OPTS_LEN]); + + $version = (($this->Elements[NET_DIME_FLAGS] >> 11) & 0x001F); + + if ($version == NET_DIME_VERSION) { + $this->OPTS_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_OPTS_LEN]); $this->ID_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_ID_LEN]); $this->TYPE_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_TYPE_LEN]); $this->DATA_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_DATA_LEN]); - + $datalen = strlen($data); - $this->Elements[NET_DIME_OPTS] = substr($data,$p,$this->Elements[NET_DIME_OPTS_LEN]); + $this->Elements[NET_DIME_OPTS] = substr($data, $p, $this->Elements[NET_DIME_OPTS_LEN]); $this->_haveOpts = (strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[NET_DIME_OPTS_LEN]); if ($this->_haveOpts) { - $p += $this->OPTS_LENGTH; - $this->Elements[NET_DIME_ID] = substr($data,$p,$this->Elements[NET_DIME_ID_LEN]); + $p += $this->OPTS_LENGTH; + $this->Elements[NET_DIME_ID] = substr($data, $p, $this->Elements[NET_DIME_ID_LEN]); $this->_haveID = (strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]); if ($this->_haveID) { $p += $this->ID_LENGTH; - $this->Elements[NET_DIME_TYPE] = substr($data,$p,$this->Elements[NET_DIME_TYPE_LEN]); + $this->Elements[NET_DIME_TYPE] = substr($data, $p, $this->Elements[NET_DIME_TYPE_LEN]); $this->_haveType = (strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]); if ($this->_haveType) { $p += $this->TYPE_LENGTH; - $this->Elements[NET_DIME_DATA] = substr($data,$p,$this->Elements[NET_DIME_DATA_LEN]); + $this->Elements[NET_DIME_DATA] = substr($data, $p, $this->Elements[NET_DIME_DATA_LEN]); $this->_haveData = (strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]); if ($this->_haveData) { $p += $this->DATA_LENGTH; @@ -277,57 +297,82 @@ class Net_DIME_Record extends PEAR $p += strlen($this->Elements[NET_DIME_ID]); } } else { - $p += strlen($this->Elements[NET_DIME_OPTS]); + $p += strlen($this->Elements[NET_DIME_OPTS]); } } return substr($data, $p); } - - function addData(&$data) + + function addData($data) { $datalen = strlen($data); $p = 0; if (!$this->_haveOpts) { $have = strlen($this->Elements[NET_DIME_OPTS]); - $this->Elements[NET_DIME_OPTS] .= substr($data,$p,$this->Elements[NET_DIME_OPTS_LEN]-$have); - $this->_haveOpts = (strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[DIME_OTPS_LEN]); - if (!$this->_haveOpts) return NULL; - $p += $this->OPTS_LENGTH-$have; + $this->Elements[NET_DIME_OPTS] .= substr($data, $p, $this->Elements[NET_DIME_OPTS_LEN] - $have); + $this->_haveOpts = strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[NET_DIME_OPTS_LEN]; + if (!$this->_haveOpts) { + return null; + } + $p += $this->OPTS_LENGTH - $have; } if (!$this->_haveID) { $have = strlen($this->Elements[NET_DIME_ID]); - $this->Elements[NET_DIME_ID] .= substr($data,$p,$this->Elements[NET_DIME_ID_LEN]-$have); - $this->_haveID = (strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]); - if (!$this->_haveID) return NULL; - $p += $this->ID_LENGTH-$have; + $this->Elements[NET_DIME_ID] .= substr($data, $p, $this->Elements[NET_DIME_ID_LEN] - $have); + $this->_haveID = strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]; + if (!$this->_haveID) { + return null; + } + $p += $this->ID_LENGTH - $have; } if (!$this->_haveType && $p < $datalen) { $have = strlen($this->Elements[NET_DIME_TYPE]); - $this->Elements[NET_DIME_TYPE] .= substr($data,$p,$this->Elements[NET_DIME_TYPE_LEN]-$have); - $this->_haveType = (strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]); - if (!$this->_haveType) return NULL; - $p += $this->TYPE_LENGTH-$have; + $this->Elements[NET_DIME_TYPE] .= substr($data, $p, $this->Elements[NET_DIME_TYPE_LEN] - $have); + $this->_haveType = strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]; + if (!$this->_haveType) { + return null; + } + $p += $this->TYPE_LENGTH - $have; } if (!$this->_haveData && $p < $datalen) { $have = strlen($this->Elements[NET_DIME_DATA]); - $this->Elements[NET_DIME_DATA] .= substr($data,$p,$this->Elements[NET_DIME_DATA_LEN]-$have); - $this->_haveData = (strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]); - if (!$this->_haveData) return NULL; - $p += $this->DATA_LENGTH-$have; + $this->Elements[NET_DIME_DATA] .= substr($data, $p, $this->Elements[NET_DIME_DATA_LEN] - $have); + $this->_haveData = strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]; + if (!$this->_haveData) { + return null; + } + $p += $this->DATA_LENGTH - $have; } - return substr($data,$p); - } + return substr($data, $p); + } } - -class Net_DIME_Message extends PEAR -{ +/** + * Net_DIME_Message enables you to manipulate and build a DIME encapsulated + * message. + * + * @category Networking + * @package Net_DIME + * @author Shane Caraveo + * @author Ralf Hofmann + * @author Jan Schneider + * @copyright 2002-2007 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @see Net_DIME_Message + * @link http://pear.php.net/package/Net_DIME + * @link http://www.ietf.org/internet-drafts/draft-nielsen-dime-02.txt + * @example test/dime_message_test.php For example of usage. + * @todo - Lots of stuff needs to be tested. + * - Definitely have to go through DIME spec and make things work + * right, most importantly, sec 3.3. + * - Make examples, document. + */ +class Net_DIME_Message { var $record_size = 4096; - #var $records =array(); var $parts = array(); var $currentPart = -1; - var $stream = NULL; + var $stream = null; var $_currentRecord; var $_proc = array(); var $type; @@ -335,43 +380,52 @@ class Net_DIME_Message extends PEAR var $mb = 1; var $me = 0; var $cf = 0; - var $id = NULL; - var $debug = FALSE; + var $id = null; + var $debug = false; + /** - * constructor + * Constructor. * - * this currently takes a file pointer as provided - * by fopen + * @todo Integrate with PHP streams. * - * TODO: integrate with the php streams stuff + * @param resource $stream A file pointer. + * @param integer $record_size + * @param boolean $debug */ - function Net_DIME_Message($stream=NULL, $record_size = 4096, $debug = FALSE) + function Net_DIME_Message($stream = null, $record_size = 4096, + $debug = false) { $this->stream = $stream; $this->record_size = $record_size; $this->debug = $debug; } - - function _makeRecord(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + + function _makeRecord($data, $typestr = '', $id = null, + $type = NET_DIME_TYPE_UNKNOWN) { $record = new Net_DIME_Record($this->debug); if ($this->mb) { $record->setMB(); // all subsequent records are not message begin! - $this->mb = 0; + $this->mb = 0; + } + if ($this->me) { + $record->setME(); + } + if ($this->cf) { + $record->setCF(); } - if ($this->me) $record->setME(); - if ($this->cf) $record->setCF(); $record->setData($data); $record->setType($typestr,$type); - if ($id) $record->setID($id); - #if ($this->debug) { - # print str_replace('\0','*',$record->encode()); - #} + if ($id) { + $record->setID($id); + } + return $record->encode(); } - - function startChunk(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + + function startChunk($data, $typestr = '', $id = null, + $type = NET_DIME_TYPE_UNKNOWN) { $this->me = 0; $this->cf = 1; @@ -382,54 +436,61 @@ class Net_DIME_Message extends PEAR } else { $this->id = md5(time()); } + return $this->_makeRecord($data, $this->typestr, $this->id, $this->type); } - function doChunk(&$data) + function doChunk($data) { $this->me = 0; $this->cf = 1; - return $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_UNCHANGED); + + return $this->_makeRecord($data, null, null, NET_DIME_TYPE_UNCHANGED); } function endChunk() { $this->cf = 0; - $data = NULL; - $rec = $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_UNCHANGED); + $data = null; + $rec = $this->_makeRecord($data, null, null, NET_DIME_TYPE_UNCHANGED); $this->id = 0; $this->cf = 0; $this->id = 0; $this->type = NET_DIME_TYPE_UNKNOWN; - $this->typestr = NULL; + $this->typestr = null; + return $rec; } - + function endMessage() { $this->me = 1; - $data = NULL; - $rec = $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_NONE); + $data = null; + $rec = $this->_makeRecord($data, null, null, NET_DIME_TYPE_NONE); $this->me = 0; $this->mb = 1; $this->id = 0; + return $rec; } - + /** - * sendRecord - * - * given a chunk of data, it creates DIME records - * and writes them to the stream + * Creates DIME records from a chunk of data and writes them to the stream + * provided in the constructor. * + * @param string $data + * @param string $typestr + * @param string $id + * @param integer $type One of the NET_DIME_TYPE_* constants. */ - function sendData(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + function sendData($data, $typestr='', $id = null, + $type = NET_DIME_TYPE_UNKNOWN) { $len = strlen($data); if ($len > $this->record_size) { $chunk = substr($data, 0, $this->record_size); $p = $this->record_size; - $rec = $this->startChunk($chunk,$typestr,$id,$type); + $rec = $this->startChunk($chunk, $typestr, $id, $type); fwrite($this->stream, $rec); while ($p < $len) { $chunk = substr($data, $p, $this->record_size); @@ -441,32 +502,35 @@ class Net_DIME_Message extends PEAR fwrite($this->stream, $rec); return; } - $rec = $this->_makeRecord($data, $typestr,$id,$type); + $rec = $this->_makeRecord($data, $typestr, $id, $type); fwrite($this->stream, $rec); } - + function sendEndMessage() { $rec = $this->endMessage(); fwrite($this->stream, $rec); } - + /** - * sendFile - * - * given a filename, it reads the file, - * creates records and writes them to the stream + * Reads a file, creates records and writes them to the stream provided in + * the constructor. * + * @param string $filename A file name. + * @param string $typestr + * @param string $id + * @param integer $type One of the NET_DIME_TYPE_* constants. */ - function sendFile($filename, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + function sendFile($filename, $typestr='', $id = null, + $type = NET_DIME_TYPE_UNKNOWN) { - $f = fopen($filename, "rb"); + $f = fopen($filename, 'rb'); if ($f) { if ($data = fread($f, $this->record_size)) { - $this->startChunk($data,$typestr,$id,$type); + $this->startChunk($data, $typestr, $id, $type); } while ($data = fread($f, $this->record_size)) { - $this->doChunk($data,$typestr,$id,$type); + $this->doChunk($data, $typestr, $id, $type); } $this->endChunk(); fclose($f); @@ -474,19 +538,23 @@ class Net_DIME_Message extends PEAR } /** - * encodeData - * - * given data, encode it in DIME + * Encodes data in DIME. * + * @param string $data + * @param string $typestr + * @param string $id + * @param integer $type One of the NET_DIME_TYPE_* constants. */ - function encodeData($data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + function encodeData($data, $typestr = '', $id = null, + $type = NET_DIME_TYPE_UNKNOWN) { $len = strlen($data); $resp = ''; + if ($len > $this->record_size) { $chunk = substr($data, 0, $this->record_size); $p = $this->record_size; - $resp .= $this->startChunk($chunk,$typestr,$id,$type); + $resp .= $this->startChunk($chunk, $typestr, $id, $type); while ($p < $len) { $chunk = substr($data, $p, $this->record_size); $p += $this->record_size; @@ -494,63 +562,62 @@ class Net_DIME_Message extends PEAR } $resp .= $this->endChunk(); } else { - $resp .= $this->_makeRecord($data, $typestr,$id,$type); + $resp .= $this->_makeRecord($data, $typestr, $id, $type); } + return $resp; } /** - * sendFile - * - * given a filename, it reads the file, - * creates records and writes them to the stream + * Reads a file, creates records and writes them to the stream provided in + * the constructor. * */ - function encodeFile($filename, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + function encodeFile($filename, $typestr = '', $id = null, + $type = NET_DIME_TYPE_UNKNOWN) { - $f = fopen($filename, "rb"); + $f = fopen($filename, 'rb'); if ($f) { if ($data = fread($f, $this->record_size)) { - $resp = $this->startChunk($data,$typestr,$id,$type); + $resp = $this->startChunk($data, $typestr, $id, $type); } while ($data = fread($f, $this->record_size)) { - $resp = $this->doChunk($data,$typestr,$id,$type); + $resp = $this->doChunk($data, $typestr, $id, $type); } $resp = $this->endChunk(); fclose($f); } + return $resp; } - + /** - * _processData - * - * creates Net_DIME_Records from provided data - * + * Creates Net_DIME_Records from provided data. */ function _processData(&$data) { - $leftover = NULL; + $leftover = null; if (!$this->_currentRecord) { $this->_currentRecord = new Net_DIME_Record($this->debug); $data = $this->_currentRecord->decode($data); } else { $data = $this->_currentRecord->addData($data); } - + if ($this->_currentRecord->_haveData) { - if (count($this->parts)==0 && !$this->_currentRecord->isStart()) { - // raise an error! + if (count($this->parts) == 0 && + !$this->_currentRecord->isStart()) { return PEAR::raiseError('First Message is not a DIME begin record!'); } - if ($this->_currentRecord->isEnd() && $this->_currentRecord->getDataLength()==0) { - return NULL; + if ($this->_currentRecord->isEnd() && + $this->_currentRecord->getDataLength() == 0) { + return; } - + if ($this->currentPart < 0 && !$this->_currentRecord->isChunk()) { $this->parts[] = array(); - $this->currentPart = count($this->parts)-1; + $this->currentPart = count($this->parts) - 1; $this->parts[$this->currentPart]['id'] = $this->_currentRecord->getID(); $this->parts[$this->currentPart]['type'] = $this->_currentRecord->getType(); $this->parts[$this->currentPart]['data'] = $this->_currentRecord->getData(); @@ -558,31 +625,31 @@ class Net_DIME_Message extends PEAR } else { if ($this->currentPart < 0) { $this->parts[] = array(); - $this->currentPart = count($this->parts)-1; + $this->currentPart = count($this->parts) - 1; $this->parts[$this->currentPart]['id'] = $this->_currentRecord->getID(); $this->parts[$this->currentPart]['type'] = $this->_currentRecord->getType(); $this->parts[$this->currentPart]['data'] = $this->_currentRecord->getData(); } else { $this->parts[$this->currentPart]['data'] .= $this->_currentRecord->getData(); if (!$this->_currentRecord->isChunk()) { - // we reached the end of the chunk + // We reached the end of the chunk. $this->currentPart = -1; } } } - #$this->records[] = $this->_currentRecord; - if (!$this->_currentRecord->isEnd()) $this->_currentRecord = NULL; + if (!$this->_currentRecord->isEnd()) { + $this->_currentRecord = null; + } } - return NULL; + + return; } - + /** - * decodeData - * - * decodes a DIME encrypted string of data - * + * Decodes a DIME encode string of data. */ - function decodeData(&$data) { + function decodeData(&$data) + { while (strlen($data) >= NET_DIME_RECORD_HEADER) { $err = $this->_processData($data); if (PEAR::isError($err)) { @@ -590,40 +657,37 @@ class Net_DIME_Message extends PEAR } } } - + /** - * read - * - * reads the stream and creates - * an array of records + * Reads the stream and creates an array of records. * - * it can accept the start of a previously read buffer - * this is usefull in situations where you need to read - * headers before discovering that the data is DIME encoded - * such as in the case of reading an HTTP response. + * The method can accept the start of a previously read buffer. This is + * useful in situations where you need to read headers before discovering + * that the data is DIME encoded, such as in the case of reading an HTTP + * response. */ - function read($buf=NULL) + function read($buf = null) { while ($data = fread($this->stream, 8192)) { if ($buf) { - $data = $buf.$data; - $buf = NULL; + $data = $buf . $data; + $buf = null; + } + if ($this->debug) { + echo 'read: ' . strlen($data) . " bytes\n"; } - if ($this->debug) - echo "read: ".strlen($data)." bytes\n"; $err = $this->decodeData($data); if (PEAR::isError($err)) { return $err; } - - // store any leftover data to be used again - // should be < NET_DIME_RECORD_HEADER bytes + + // Store any leftover data to be used again. + // Should be < NET_DIME_RECORD_HEADER bytes. $buf = $data; } + if (!$this->_currentRecord || !$this->_currentRecord->isEnd()) { return PEAR::raiseError('reached stream end without end record'); } - return NULL; } } -?> \ No newline at end of file diff --git a/thirdparty/pear/Net/Dict.php b/thirdparty/pear/Net/Dict.php old mode 100644 new mode 100755 index cf7c4ce..cf7c4ce --- a/thirdparty/pear/Net/Dict.php +++ b/thirdparty/pear/Net/Dict.php diff --git a/thirdparty/pear/Net/Dig.php b/thirdparty/pear/Net/Dig.php old mode 100644 new mode 100755 index 2721b8d..2721b8d --- a/thirdparty/pear/Net/Dig.php +++ b/thirdparty/pear/Net/Dig.php diff --git a/thirdparty/pear/Net/Finger.php b/thirdparty/pear/Net/Finger.php old mode 100644 new mode 100755 index ea61dd9..ea61dd9 --- a/thirdparty/pear/Net/Finger.php +++ b/thirdparty/pear/Net/Finger.php diff --git a/thirdparty/pear/Net/Geo.php b/thirdparty/pear/Net/Geo.php old mode 100644 new mode 100755 index 6d19a7c..6d19a7c --- a/thirdparty/pear/Net/Geo.php +++ b/thirdparty/pear/Net/Geo.php diff --git a/thirdparty/pear/Net/IDNA.php b/thirdparty/pear/Net/IDNA.php new file mode 100644 index 0000000..14749d5 --- /dev/null +++ b/thirdparty/pear/Net/IDNA.php @@ -0,0 +1,101 @@ + + * @author Matthias Sommerfeld + * @package Net + * @version $Id: IDNA.php,v 1.1 2008/03/22 12:39:37 neufeind Exp $ + */ + +class Net_IDNA +{ + // {{{ factory + /** + * Attempts to return a concrete IDNA instance for either php4 or php5. + * + * @param array $params Set of paramaters + * @return object IDNA The newly created concrete Log instance, or an + * false on an error. + * @access public + */ + function &getInstance($params = array()) + { + $version = explode( '.', phpversion() ); + $handler = ((int)$version[0] > 4) ? 'php5' : 'php4'; + $class = 'Net_IDNA_' . $handler; + $classfile = 'Net/IDNA/' . $handler . '.php'; + + /* + * Attempt to include our version of the named class, but don't treat + * a failure as fatal. The caller may have already included their own + * version of the named class. + */ + @include_once $classfile; + + /* If the class exists, return a new instance of it. */ + if (class_exists($class)) { + $instance = &new $class($params); + return $instance; + } + + return false; + } + // }}} + + // {{{ singleton + /** + * Attempts to return a concrete IDNA instance for either php4 or php5, + * only creating a new instance if no IDNA instance with the same + * parameters currently exists. + * + * @param array $params Set of paramaters + * @return object IDNA The newly created concrete Log instance, or an + * false on an error. + * @access public + */ + function &singleton($params = array()) + { + static $instances; + if (!isset($instances)) { + $instances = array(); + } + + $signature = serialize($params); + if (!isset($instances[$signature])) { + $instances[$signature] = &Net_IDNA::getInstance($params); + } + + return $instances[$signature]; + } + // }}} +} + +?> diff --git a/thirdparty/pear/Net/IDNA/php4.php b/thirdparty/pear/Net/IDNA/php4.php new file mode 100644 index 0000000..3e93bb4 --- /dev/null +++ b/thirdparty/pear/Net/IDNA/php4.php @@ -0,0 +1,3011 @@ + + * @author Matthias Sommerfeld + * @author Stefan Neufeind + * @package Net + * @version $Id: php4.php,v 1.3 2008/03/22 15:24:17 neufeind Exp $ + */ + +class Net_IDNA_php4 +{ + // {{{ npdata + /** + * These Unicode codepoints are mapped to nothing, + * See RFC3454 for details + * + * @static + * @var array + * @access private + */ + var $_np_map_nothing = array( + 0xAD, + 0x34F, + 0x1806, + 0x180B, + 0x180C, + 0x180D, + 0x200B, + 0x200C, + 0x200D, + 0x2060, + 0xFE00, + 0xFE01, + 0xFE02, + 0xFE03, + 0xFE04, + 0xFE05, + 0xFE06, + 0xFE07, + 0xFE08, + 0xFE09, + 0xFE0A, + 0xFE0B, + 0xFE0C, + 0xFE0D, + 0xFE0E, + 0xFE0F, + 0xFEFF + ); + + /** + * @static + * @var array + * @access private + */ + var $_general_prohibited = array( + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0xA, + 0xB, + 0xC, + 0xD, + 0xE, + 0xF, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x2A, + 0x2B, + 0x2C, + 0x2F, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + 0x40, + 0x5B, + 0x5C, + 0x5D, + 0x5E, + 0x5F, + 0x60, + 0x7B, + 0x7C, + 0x7D, + 0x7E, + 0x7F, + 0x3002 + ); + + /** + * @static + * @var array + * @access private + */ + var $_np_prohibit = array( + 0xA0, + 0x1680, + 0x2000, + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200A, + 0x200B, + 0x202F, + 0x205F, + 0x3000, + 0x6DD, + 0x70F, + 0x180E, + 0x200C, + 0x200D, + 0x2028, + 0x2029, + 0xFEFF, + 0xFFF9, + 0xFFFA, + 0xFFFB, + 0xFFFC, + 0xFFFE, + 0xFFFF, + 0x1FFFE, + 0x1FFFF, + 0x2FFFE, + 0x2FFFF, + 0x3FFFE, + 0x3FFFF, + 0x4FFFE, + 0x4FFFF, + 0x5FFFE, + 0x5FFFF, + 0x6FFFE, + 0x6FFFF, + 0x7FFFE, + 0x7FFFF, + 0x8FFFE, + 0x8FFFF, + 0x9FFFE, + 0x9FFFF, + 0xAFFFE, + 0xAFFFF, + 0xBFFFE, + 0xBFFFF, + 0xCFFFE, + 0xCFFFF, + 0xDFFFE, + 0xDFFFF, + 0xEFFFE, + 0xEFFFF, + 0xFFFFE, + 0xFFFFF, + 0x10FFFE, + 0x10FFFF, + 0xFFF9, + 0xFFFA, + 0xFFFB, + 0xFFFC, + 0xFFFD, + 0x340, + 0x341, + 0x200E, + 0x200F, + 0x202A, + 0x202B, + 0x202C, + 0x202D, + 0x202E, + 0x206A, + 0x206B, + 0x206C, + 0x206D, + 0x206E, + 0x206F, + 0xE0001 + ); + + /** + * @static + * @var array + * @access private + */ + var $_np_prohibit_ranges = array( + array(0x80, 0x9F ), + array(0x2060, 0x206F ), + array(0x1D173, 0x1D17A ), + array(0xE000, 0xF8FF ), + array(0xF0000, 0xFFFFD ), + array(0x100000, 0x10FFFD), + array(0xFDD0, 0xFDEF ), + array(0xD800, 0xDFFF ), + array(0x2FF0, 0x2FFB ), + array(0xE0020, 0xE007F ) + ); + + /** + * @static + * @var array + * @access private + */ + var $_np_replacemaps = array( + 0x41 => array(0x61), + 0x42 => array(0x62), + 0x43 => array(0x63), + 0x44 => array(0x64), + 0x45 => array(0x65), + 0x46 => array(0x66), + 0x47 => array(0x67), + 0x48 => array(0x68), + 0x49 => array(0x69), + 0x4A => array(0x6A), + 0x4B => array(0x6B), + 0x4C => array(0x6C), + 0x4D => array(0x6D), + 0x4E => array(0x6E), + 0x4F => array(0x6F), + 0x50 => array(0x70), + 0x51 => array(0x71), + 0x52 => array(0x72), + 0x53 => array(0x73), + 0x54 => array(0x74), + 0x55 => array(0x75), + 0x56 => array(0x76), + 0x57 => array(0x77), + 0x58 => array(0x78), + 0x59 => array(0x79), + 0x5A => array(0x7A), + 0xB5 => array(0x3BC), + 0xC0 => array(0xE0), + 0xC1 => array(0xE1), + 0xC2 => array(0xE2), + 0xC3 => array(0xE3), + 0xC4 => array(0xE4), + 0xC5 => array(0xE5), + 0xC6 => array(0xE6), + 0xC7 => array(0xE7), + 0xC8 => array(0xE8), + 0xC9 => array(0xE9), + 0xCA => array(0xEA), + 0xCB => array(0xEB), + 0xCC => array(0xEC), + 0xCD => array(0xED), + 0xCE => array(0xEE), + 0xCF => array(0xEF), + 0xD0 => array(0xF0), + 0xD1 => array(0xF1), + 0xD2 => array(0xF2), + 0xD3 => array(0xF3), + 0xD4 => array(0xF4), + 0xD5 => array(0xF5), + 0xD6 => array(0xF6), + 0xD8 => array(0xF8), + 0xD9 => array(0xF9), + 0xDA => array(0xFA), + 0xDB => array(0xFB), + 0xDC => array(0xFC), + 0xDD => array(0xFD), + 0xDE => array(0xFE), + 0xDF => array(0x73, 0x73), + 0x100 => array(0x101), + 0x102 => array(0x103), + 0x104 => array(0x105), + 0x106 => array(0x107), + 0x108 => array(0x109), + 0x10A => array(0x10B), + 0x10C => array(0x10D), + 0x10E => array(0x10F), + 0x110 => array(0x111), + 0x112 => array(0x113), + 0x114 => array(0x115), + 0x116 => array(0x117), + 0x118 => array(0x119), + 0x11A => array(0x11B), + 0x11C => array(0x11D), + 0x11E => array(0x11F), + 0x120 => array(0x121), + 0x122 => array(0x123), + 0x124 => array(0x125), + 0x126 => array(0x127), + 0x128 => array(0x129), + 0x12A => array(0x12B), + 0x12C => array(0x12D), + 0x12E => array(0x12F), + 0x130 => array(0x69, 0x307), + 0x132 => array(0x133), + 0x134 => array(0x135), + 0x136 => array(0x137), + 0x139 => array(0x13A), + 0x13B => array(0x13C), + 0x13D => array(0x13E), + 0x13F => array(0x140), + 0x141 => array(0x142), + 0x143 => array(0x144), + 0x145 => array(0x146), + 0x147 => array(0x148), + 0x149 => array(0x2BC, 0x6E), + 0x14A => array(0x14B), + 0x14C => array(0x14D), + 0x14E => array(0x14F), + 0x150 => array(0x151), + 0x152 => array(0x153), + 0x154 => array(0x155), + 0x156 => array(0x157), + 0x158 => array(0x159), + 0x15A => array(0x15B), + 0x15C => array(0x15D), + 0x15E => array(0x15F), + 0x160 => array(0x161), + 0x162 => array(0x163), + 0x164 => array(0x165), + 0x166 => array(0x167), + 0x168 => array(0x169), + 0x16A => array(0x16B), + 0x16C => array(0x16D), + 0x16E => array(0x16F), + 0x170 => array(0x171), + 0x172 => array(0x173), + 0x174 => array(0x175), + 0x176 => array(0x177), + 0x178 => array(0xFF), + 0x179 => array(0x17A), + 0x17B => array(0x17C), + 0x17D => array(0x17E), + 0x17F => array(0x73), + 0x181 => array(0x253), + 0x182 => array(0x183), + 0x184 => array(0x185), + 0x186 => array(0x254), + 0x187 => array(0x188), + 0x189 => array(0x256), + 0x18A => array(0x257), + 0x18B => array(0x18C), + 0x18E => array(0x1DD), + 0x18F => array(0x259), + 0x190 => array(0x25B), + 0x191 => array(0x192), + 0x193 => array(0x260), + 0x194 => array(0x263), + 0x196 => array(0x269), + 0x197 => array(0x268), + 0x198 => array(0x199), + 0x19C => array(0x26F), + 0x19D => array(0x272), + 0x19F => array(0x275), + 0x1A0 => array(0x1A1), + 0x1A2 => array(0x1A3), + 0x1A4 => array(0x1A5), + 0x1A6 => array(0x280), + 0x1A7 => array(0x1A8), + 0x1A9 => array(0x283), + 0x1AC => array(0x1AD), + 0x1AE => array(0x288), + 0x1AF => array(0x1B0), + 0x1B1 => array(0x28A), + 0x1B2 => array(0x28B), + 0x1B3 => array(0x1B4), + 0x1B5 => array(0x1B6), + 0x1B7 => array(0x292), + 0x1B8 => array(0x1B9), + 0x1BC => array(0x1BD), + 0x1C4 => array(0x1C6), + 0x1C5 => array(0x1C6), + 0x1C7 => array(0x1C9), + 0x1C8 => array(0x1C9), + 0x1CA => array(0x1CC), + 0x1CB => array(0x1CC), + 0x1CD => array(0x1CE), + 0x1CF => array(0x1D0), + 0x1D1 => array(0x1D2), + 0x1D3 => array(0x1D4), + 0x1D5 => array(0x1D6), + 0x1D7 => array(0x1D8), + 0x1D9 => array(0x1DA), + 0x1DB => array(0x1DC), + 0x1DE => array(0x1DF), + 0x1E0 => array(0x1E1), + 0x1E2 => array(0x1E3), + 0x1E4 => array(0x1E5), + 0x1E6 => array(0x1E7), + 0x1E8 => array(0x1E9), + 0x1EA => array(0x1EB), + 0x1EC => array(0x1ED), + 0x1EE => array(0x1EF), + 0x1F0 => array(0x6A, 0x30C), + 0x1F1 => array(0x1F3), + 0x1F2 => array(0x1F3), + 0x1F4 => array(0x1F5), + 0x1F6 => array(0x195), + 0x1F7 => array(0x1BF), + 0x1F8 => array(0x1F9), + 0x1FA => array(0x1FB), + 0x1FC => array(0x1FD), + 0x1FE => array(0x1FF), + 0x200 => array(0x201), + 0x202 => array(0x203), + 0x204 => array(0x205), + 0x206 => array(0x207), + 0x208 => array(0x209), + 0x20A => array(0x20B), + 0x20C => array(0x20D), + 0x20E => array(0x20F), + 0x210 => array(0x211), + 0x212 => array(0x213), + 0x214 => array(0x215), + 0x216 => array(0x217), + 0x218 => array(0x219), + 0x21A => array(0x21B), + 0x21C => array(0x21D), + 0x21E => array(0x21F), + 0x220 => array(0x19E), + 0x222 => array(0x223), + 0x224 => array(0x225), + 0x226 => array(0x227), + 0x228 => array(0x229), + 0x22A => array(0x22B), + 0x22C => array(0x22D), + 0x22E => array(0x22F), + 0x230 => array(0x231), + 0x232 => array(0x233), + 0x345 => array(0x3B9), + 0x37A => array(0x20, 0x3B9), + 0x386 => array(0x3AC), + 0x388 => array(0x3AD), + 0x389 => array(0x3AE), + 0x38A => array(0x3AF), + 0x38C => array(0x3CC), + 0x38E => array(0x3CD), + 0x38F => array(0x3CE), + 0x390 => array(0x3B9, 0x308, 0x301), + 0x391 => array(0x3B1), + 0x392 => array(0x3B2), + 0x393 => array(0x3B3), + 0x394 => array(0x3B4), + 0x395 => array(0x3B5), + 0x396 => array(0x3B6), + 0x397 => array(0x3B7), + 0x398 => array(0x3B8), + 0x399 => array(0x3B9), + 0x39A => array(0x3BA), + 0x39B => array(0x3BB), + 0x39C => array(0x3BC), + 0x39D => array(0x3BD), + 0x39E => array(0x3BE), + 0x39F => array(0x3BF), + 0x3A0 => array(0x3C0), + 0x3A1 => array(0x3C1), + 0x3A3 => array(0x3C3), + 0x3A4 => array(0x3C4), + 0x3A5 => array(0x3C5), + 0x3A6 => array(0x3C6), + 0x3A7 => array(0x3C7), + 0x3A8 => array(0x3C8), + 0x3A9 => array(0x3C9), + 0x3AA => array(0x3CA), + 0x3AB => array(0x3CB), + 0x3B0 => array(0x3C5, 0x308, 0x301), + 0x3C2 => array(0x3C3), + 0x3D0 => array(0x3B2), + 0x3D1 => array(0x3B8), + 0x3D2 => array(0x3C5), + 0x3D3 => array(0x3CD), + 0x3D4 => array(0x3CB), + 0x3D5 => array(0x3C6), + 0x3D6 => array(0x3C0), + 0x3D8 => array(0x3D9), + 0x3DA => array(0x3DB), + 0x3DC => array(0x3DD), + 0x3DE => array(0x3DF), + 0x3E0 => array(0x3E1), + 0x3E2 => array(0x3E3), + 0x3E4 => array(0x3E5), + 0x3E6 => array(0x3E7), + 0x3E8 => array(0x3E9), + 0x3EA => array(0x3EB), + 0x3EC => array(0x3ED), + 0x3EE => array(0x3EF), + 0x3F0 => array(0x3BA), + 0x3F1 => array(0x3C1), + 0x3F2 => array(0x3C3), + 0x3F4 => array(0x3B8), + 0x3F5 => array(0x3B5), + 0x400 => array(0x450), + 0x401 => array(0x451), + 0x402 => array(0x452), + 0x403 => array(0x453), + 0x404 => array(0x454), + 0x405 => array(0x455), + 0x406 => array(0x456), + 0x407 => array(0x457), + 0x408 => array(0x458), + 0x409 => array(0x459), + 0x40A => array(0x45A), + 0x40B => array(0x45B), + 0x40C => array(0x45C), + 0x40D => array(0x45D), + 0x40E => array(0x45E), + 0x40F => array(0x45F), + 0x410 => array(0x430), + 0x411 => array(0x431), + 0x412 => array(0x432), + 0x413 => array(0x433), + 0x414 => array(0x434), + 0x415 => array(0x435), + 0x416 => array(0x436), + 0x417 => array(0x437), + 0x418 => array(0x438), + 0x419 => array(0x439), + 0x41A => array(0x43A), + 0x41B => array(0x43B), + 0x41C => array(0x43C), + 0x41D => array(0x43D), + 0x41E => array(0x43E), + 0x41F => array(0x43F), + 0x420 => array(0x440), + 0x421 => array(0x441), + 0x422 => array(0x442), + 0x423 => array(0x443), + 0x424 => array(0x444), + 0x425 => array(0x445), + 0x426 => array(0x446), + 0x427 => array(0x447), + 0x428 => array(0x448), + 0x429 => array(0x449), + 0x42A => array(0x44A), + 0x42B => array(0x44B), + 0x42C => array(0x44C), + 0x42D => array(0x44D), + 0x42E => array(0x44E), + 0x42F => array(0x44F), + 0x460 => array(0x461), + 0x462 => array(0x463), + 0x464 => array(0x465), + 0x466 => array(0x467), + 0x468 => array(0x469), + 0x46A => array(0x46B), + 0x46C => array(0x46D), + 0x46E => array(0x46F), + 0x470 => array(0x471), + 0x472 => array(0x473), + 0x474 => array(0x475), + 0x476 => array(0x477), + 0x478 => array(0x479), + 0x47A => array(0x47B), + 0x47C => array(0x47D), + 0x47E => array(0x47F), + 0x480 => array(0x481), + 0x48A => array(0x48B), + 0x48C => array(0x48D), + 0x48E => array(0x48F), + 0x490 => array(0x491), + 0x492 => array(0x493), + 0x494 => array(0x495), + 0x496 => array(0x497), + 0x498 => array(0x499), + 0x49A => array(0x49B), + 0x49C => array(0x49D), + 0x49E => array(0x49F), + 0x4A0 => array(0x4A1), + 0x4A2 => array(0x4A3), + 0x4A4 => array(0x4A5), + 0x4A6 => array(0x4A7), + 0x4A8 => array(0x4A9), + 0x4AA => array(0x4AB), + 0x4AC => array(0x4AD), + 0x4AE => array(0x4AF), + 0x4B0 => array(0x4B1), + 0x4B2 => array(0x4B3), + 0x4B4 => array(0x4B5), + 0x4B6 => array(0x4B7), + 0x4B8 => array(0x4B9), + 0x4BA => array(0x4BB), + 0x4BC => array(0x4BD), + 0x4BE => array(0x4BF), + 0x4C1 => array(0x4C2), + 0x4C3 => array(0x4C4), + 0x4C5 => array(0x4C6), + 0x4C7 => array(0x4C8), + 0x4C9 => array(0x4CA), + 0x4CB => array(0x4CC), + 0x4CD => array(0x4CE), + 0x4D0 => array(0x4D1), + 0x4D2 => array(0x4D3), + 0x4D4 => array(0x4D5), + 0x4D6 => array(0x4D7), + 0x4D8 => array(0x4D9), + 0x4DA => array(0x4DB), + 0x4DC => array(0x4DD), + 0x4DE => array(0x4DF), + 0x4E0 => array(0x4E1), + 0x4E2 => array(0x4E3), + 0x4E4 => array(0x4E5), + 0x4E6 => array(0x4E7), + 0x4E8 => array(0x4E9), + 0x4EA => array(0x4EB), + 0x4EC => array(0x4ED), + 0x4EE => array(0x4EF), + 0x4F0 => array(0x4F1), + 0x4F2 => array(0x4F3), + 0x4F4 => array(0x4F5), + 0x4F8 => array(0x4F9), + 0x500 => array(0x501), + 0x502 => array(0x503), + 0x504 => array(0x505), + 0x506 => array(0x507), + 0x508 => array(0x509), + 0x50A => array(0x50B), + 0x50C => array(0x50D), + 0x50E => array(0x50F), + 0x531 => array(0x561), + 0x532 => array(0x562), + 0x533 => array(0x563), + 0x534 => array(0x564), + 0x535 => array(0x565), + 0x536 => array(0x566), + 0x537 => array(0x567), + 0x538 => array(0x568), + 0x539 => array(0x569), + 0x53A => array(0x56A), + 0x53B => array(0x56B), + 0x53C => array(0x56C), + 0x53D => array(0x56D), + 0x53E => array(0x56E), + 0x53F => array(0x56F), + 0x540 => array(0x570), + 0x541 => array(0x571), + 0x542 => array(0x572), + 0x543 => array(0x573), + 0x544 => array(0x574), + 0x545 => array(0x575), + 0x546 => array(0x576), + 0x547 => array(0x577), + 0x548 => array(0x578), + 0x549 => array(0x579), + 0x54A => array(0x57A), + 0x54B => array(0x57B), + 0x54C => array(0x57C), + 0x54D => array(0x57D), + 0x54E => array(0x57E), + 0x54F => array(0x57F), + 0x550 => array(0x580), + 0x551 => array(0x581), + 0x552 => array(0x582), + 0x553 => array(0x583), + 0x554 => array(0x584), + 0x555 => array(0x585), + 0x556 => array(0x586), + 0x587 => array(0x565, 0x582), + 0x1E00 => array(0x1E01), + 0x1E02 => array(0x1E03), + 0x1E04 => array(0x1E05), + 0x1E06 => array(0x1E07), + 0x1E08 => array(0x1E09), + 0x1E0A => array(0x1E0B), + 0x1E0C => array(0x1E0D), + 0x1E0E => array(0x1E0F), + 0x1E10 => array(0x1E11), + 0x1E12 => array(0x1E13), + 0x1E14 => array(0x1E15), + 0x1E16 => array(0x1E17), + 0x1E18 => array(0x1E19), + 0x1E1A => array(0x1E1B), + 0x1E1C => array(0x1E1D), + 0x1E1E => array(0x1E1F), + 0x1E20 => array(0x1E21), + 0x1E22 => array(0x1E23), + 0x1E24 => array(0x1E25), + 0x1E26 => array(0x1E27), + 0x1E28 => array(0x1E29), + 0x1E2A => array(0x1E2B), + 0x1E2C => array(0x1E2D), + 0x1E2E => array(0x1E2F), + 0x1E30 => array(0x1E31), + 0x1E32 => array(0x1E33), + 0x1E34 => array(0x1E35), + 0x1E36 => array(0x1E37), + 0x1E38 => array(0x1E39), + 0x1E3A => array(0x1E3B), + 0x1E3C => array(0x1E3D), + 0x1E3E => array(0x1E3F), + 0x1E40 => array(0x1E41), + 0x1E42 => array(0x1E43), + 0x1E44 => array(0x1E45), + 0x1E46 => array(0x1E47), + 0x1E48 => array(0x1E49), + 0x1E4A => array(0x1E4B), + 0x1E4C => array(0x1E4D), + 0x1E4E => array(0x1E4F), + 0x1E50 => array(0x1E51), + 0x1E52 => array(0x1E53), + 0x1E54 => array(0x1E55), + 0x1E56 => array(0x1E57), + 0x1E58 => array(0x1E59), + 0x1E5A => array(0x1E5B), + 0x1E5C => array(0x1E5D), + 0x1E5E => array(0x1E5F), + 0x1E60 => array(0x1E61), + 0x1E62 => array(0x1E63), + 0x1E64 => array(0x1E65), + 0x1E66 => array(0x1E67), + 0x1E68 => array(0x1E69), + 0x1E6A => array(0x1E6B), + 0x1E6C => array(0x1E6D), + 0x1E6E => array(0x1E6F), + 0x1E70 => array(0x1E71), + 0x1E72 => array(0x1E73), + 0x1E74 => array(0x1E75), + 0x1E76 => array(0x1E77), + 0x1E78 => array(0x1E79), + 0x1E7A => array(0x1E7B), + 0x1E7C => array(0x1E7D), + 0x1E7E => array(0x1E7F), + 0x1E80 => array(0x1E81), + 0x1E82 => array(0x1E83), + 0x1E84 => array(0x1E85), + 0x1E86 => array(0x1E87), + 0x1E88 => array(0x1E89), + 0x1E8A => array(0x1E8B), + 0x1E8C => array(0x1E8D), + 0x1E8E => array(0x1E8F), + 0x1E90 => array(0x1E91), + 0x1E92 => array(0x1E93), + 0x1E94 => array(0x1E95), + 0x1E96 => array(0x68, 0x331), + 0x1E97 => array(0x74, 0x308), + 0x1E98 => array(0x77, 0x30A), + 0x1E99 => array(0x79, 0x30A), + 0x1E9A => array(0x61, 0x2BE), + 0x1E9B => array(0x1E61), + 0x1EA0 => array(0x1EA1), + 0x1EA2 => array(0x1EA3), + 0x1EA4 => array(0x1EA5), + 0x1EA6 => array(0x1EA7), + 0x1EA8 => array(0x1EA9), + 0x1EAA => array(0x1EAB), + 0x1EAC => array(0x1EAD), + 0x1EAE => array(0x1EAF), + 0x1EB0 => array(0x1EB1), + 0x1EB2 => array(0x1EB3), + 0x1EB4 => array(0x1EB5), + 0x1EB6 => array(0x1EB7), + 0x1EB8 => array(0x1EB9), + 0x1EBA => array(0x1EBB), + 0x1EBC => array(0x1EBD), + 0x1EBE => array(0x1EBF), + 0x1EC0 => array(0x1EC1), + 0x1EC2 => array(0x1EC3), + 0x1EC4 => array(0x1EC5), + 0x1EC6 => array(0x1EC7), + 0x1EC8 => array(0x1EC9), + 0x1ECA => array(0x1ECB), + 0x1ECC => array(0x1ECD), + 0x1ECE => array(0x1ECF), + 0x1ED0 => array(0x1ED1), + 0x1ED2 => array(0x1ED3), + 0x1ED4 => array(0x1ED5), + 0x1ED6 => array(0x1ED7), + 0x1ED8 => array(0x1ED9), + 0x1EDA => array(0x1EDB), + 0x1EDC => array(0x1EDD), + 0x1EDE => array(0x1EDF), + 0x1EE0 => array(0x1EE1), + 0x1EE2 => array(0x1EE3), + 0x1EE4 => array(0x1EE5), + 0x1EE6 => array(0x1EE7), + 0x1EE8 => array(0x1EE9), + 0x1EEA => array(0x1EEB), + 0x1EEC => array(0x1EED), + 0x1EEE => array(0x1EEF), + 0x1EF0 => array(0x1EF1), + 0x1EF2 => array(0x1EF3), + 0x1EF4 => array(0x1EF5), + 0x1EF6 => array(0x1EF7), + 0x1EF8 => array(0x1EF9), + 0x1F08 => array(0x1F00), + 0x1F09 => array(0x1F01), + 0x1F0A => array(0x1F02), + 0x1F0B => array(0x1F03), + 0x1F0C => array(0x1F04), + 0x1F0D => array(0x1F05), + 0x1F0E => array(0x1F06), + 0x1F0F => array(0x1F07), + 0x1F18 => array(0x1F10), + 0x1F19 => array(0x1F11), + 0x1F1A => array(0x1F12), + 0x1F1B => array(0x1F13), + 0x1F1C => array(0x1F14), + 0x1F1D => array(0x1F15), + 0x1F28 => array(0x1F20), + 0x1F29 => array(0x1F21), + 0x1F2A => array(0x1F22), + 0x1F2B => array(0x1F23), + 0x1F2C => array(0x1F24), + 0x1F2D => array(0x1F25), + 0x1F2E => array(0x1F26), + 0x1F2F => array(0x1F27), + 0x1F38 => array(0x1F30), + 0x1F39 => array(0x1F31), + 0x1F3A => array(0x1F32), + 0x1F3B => array(0x1F33), + 0x1F3C => array(0x1F34), + 0x1F3D => array(0x1F35), + 0x1F3E => array(0x1F36), + 0x1F3F => array(0x1F37), + 0x1F48 => array(0x1F40), + 0x1F49 => array(0x1F41), + 0x1F4A => array(0x1F42), + 0x1F4B => array(0x1F43), + 0x1F4C => array(0x1F44), + 0x1F4D => array(0x1F45), + 0x1F50 => array(0x3C5, 0x313), + 0x1F52 => array(0x3C5, 0x313, 0x300), + 0x1F54 => array(0x3C5, 0x313, 0x301), + 0x1F56 => array(0x3C5, 0x313, 0x342), + 0x1F59 => array(0x1F51), + 0x1F5B => array(0x1F53), + 0x1F5D => array(0x1F55), + 0x1F5F => array(0x1F57), + 0x1F68 => array(0x1F60), + 0x1F69 => array(0x1F61), + 0x1F6A => array(0x1F62), + 0x1F6B => array(0x1F63), + 0x1F6C => array(0x1F64), + 0x1F6D => array(0x1F65), + 0x1F6E => array(0x1F66), + 0x1F6F => array(0x1F67), + 0x1F80 => array(0x1F00, 0x3B9), + 0x1F81 => array(0x1F01, 0x3B9), + 0x1F82 => array(0x1F02, 0x3B9), + 0x1F83 => array(0x1F03, 0x3B9), + 0x1F84 => array(0x1F04, 0x3B9), + 0x1F85 => array(0x1F05, 0x3B9), + 0x1F86 => array(0x1F06, 0x3B9), + 0x1F87 => array(0x1F07, 0x3B9), + 0x1F88 => array(0x1F00, 0x3B9), + 0x1F89 => array(0x1F01, 0x3B9), + 0x1F8A => array(0x1F02, 0x3B9), + 0x1F8B => array(0x1F03, 0x3B9), + 0x1F8C => array(0x1F04, 0x3B9), + 0x1F8D => array(0x1F05, 0x3B9), + 0x1F8E => array(0x1F06, 0x3B9), + 0x1F8F => array(0x1F07, 0x3B9), + 0x1F90 => array(0x1F20, 0x3B9), + 0x1F91 => array(0x1F21, 0x3B9), + 0x1F92 => array(0x1F22, 0x3B9), + 0x1F93 => array(0x1F23, 0x3B9), + 0x1F94 => array(0x1F24, 0x3B9), + 0x1F95 => array(0x1F25, 0x3B9), + 0x1F96 => array(0x1F26, 0x3B9), + 0x1F97 => array(0x1F27, 0x3B9), + 0x1F98 => array(0x1F20, 0x3B9), + 0x1F99 => array(0x1F21, 0x3B9), + 0x1F9A => array(0x1F22, 0x3B9), + 0x1F9B => array(0x1F23, 0x3B9), + 0x1F9C => array(0x1F24, 0x3B9), + 0x1F9D => array(0x1F25, 0x3B9), + 0x1F9E => array(0x1F26, 0x3B9), + 0x1F9F => array(0x1F27, 0x3B9), + 0x1FA0 => array(0x1F60, 0x3B9), + 0x1FA1 => array(0x1F61, 0x3B9), + 0x1FA2 => array(0x1F62, 0x3B9), + 0x1FA3 => array(0x1F63, 0x3B9), + 0x1FA4 => array(0x1F64, 0x3B9), + 0x1FA5 => array(0x1F65, 0x3B9), + 0x1FA6 => array(0x1F66, 0x3B9), + 0x1FA7 => array(0x1F67, 0x3B9), + 0x1FA8 => array(0x1F60, 0x3B9), + 0x1FA9 => array(0x1F61, 0x3B9), + 0x1FAA => array(0x1F62, 0x3B9), + 0x1FAB => array(0x1F63, 0x3B9), + 0x1FAC => array(0x1F64, 0x3B9), + 0x1FAD => array(0x1F65, 0x3B9), + 0x1FAE => array(0x1F66, 0x3B9), + 0x1FAF => array(0x1F67, 0x3B9), + 0x1FB2 => array(0x1F70, 0x3B9), + 0x1FB3 => array(0x3B1, 0x3B9), + 0x1FB4 => array(0x3AC, 0x3B9), + 0x1FB6 => array(0x3B1, 0x342), + 0x1FB7 => array(0x3B1, 0x342, 0x3B9), + 0x1FB8 => array(0x1FB0), + 0x1FB9 => array(0x1FB1), + 0x1FBA => array(0x1F70), + 0x1FBB => array(0x1F71), + 0x1FBC => array(0x3B1, 0x3B9), + 0x1FBE => array(0x3B9), + 0x1FC2 => array(0x1F74, 0x3B9), + 0x1FC3 => array(0x3B7, 0x3B9), + 0x1FC4 => array(0x3AE, 0x3B9), + 0x1FC6 => array(0x3B7, 0x342), + 0x1FC7 => array(0x3B7, 0x342, 0x3B9), + 0x1FC8 => array(0x1F72), + 0x1FC9 => array(0x1F73), + 0x1FCA => array(0x1F74), + 0x1FCB => array(0x1F75), + 0x1FCC => array(0x3B7, 0x3B9), + 0x1FD2 => array(0x3B9, 0x308, 0x300), + 0x1FD3 => array(0x3B9, 0x308, 0x301), + 0x1FD6 => array(0x3B9, 0x342), + 0x1FD7 => array(0x3B9, 0x308, 0x342), + 0x1FD8 => array(0x1FD0), + 0x1FD9 => array(0x1FD1), + 0x1FDA => array(0x1F76), + 0x1FDB => array(0x1F77), + 0x1FE2 => array(0x3C5, 0x308, 0x300), + 0x1FE3 => array(0x3C5, 0x308, 0x301), + 0x1FE4 => array(0x3C1, 0x313), + 0x1FE6 => array(0x3C5, 0x342), + 0x1FE7 => array(0x3C5, 0x308, 0x342), + 0x1FE8 => array(0x1FE0), + 0x1FE9 => array(0x1FE1), + 0x1FEA => array(0x1F7A), + 0x1FEB => array(0x1F7B), + 0x1FEC => array(0x1FE5), + 0x1FF2 => array(0x1F7C, 0x3B9), + 0x1FF3 => array(0x3C9, 0x3B9), + 0x1FF4 => array(0x3CE, 0x3B9), + 0x1FF6 => array(0x3C9, 0x342), + 0x1FF7 => array(0x3C9, 0x342, 0x3B9), + 0x1FF8 => array(0x1F78), + 0x1FF9 => array(0x1F79), + 0x1FFA => array(0x1F7C), + 0x1FFB => array(0x1F7D), + 0x1FFC => array(0x3C9, 0x3B9), + 0x20A8 => array(0x72, 0x73), + 0x2102 => array(0x63), + 0x2103 => array(0xB0, 0x63), + 0x2107 => array(0x25B), + 0x2109 => array(0xB0, 0x66), + 0x210B => array(0x68), + 0x210C => array(0x68), + 0x210D => array(0x68), + 0x2110 => array(0x69), + 0x2111 => array(0x69), + 0x2112 => array(0x6C), + 0x2115 => array(0x6E), + 0x2116 => array(0x6E, 0x6F), + 0x2119 => array(0x70), + 0x211A => array(0x71), + 0x211B => array(0x72), + 0x211C => array(0x72), + 0x211D => array(0x72), + 0x2120 => array(0x73, 0x6D), + 0x2121 => array(0x74, 0x65, 0x6C), + 0x2122 => array(0x74, 0x6D), + 0x2124 => array(0x7A), + 0x2126 => array(0x3C9), + 0x2128 => array(0x7A), + 0x212A => array(0x6B), + 0x212B => array(0xE5), + 0x212C => array(0x62), + 0x212D => array(0x63), + 0x2130 => array(0x65), + 0x2131 => array(0x66), + 0x2133 => array(0x6D), + 0x213E => array(0x3B3), + 0x213F => array(0x3C0), + 0x2145 => array(0x64), + 0x2160 => array(0x2170), + 0x2161 => array(0x2171), + 0x2162 => array(0x2172), + 0x2163 => array(0x2173), + 0x2164 => array(0x2174), + 0x2165 => array(0x2175), + 0x2166 => array(0x2176), + 0x2167 => array(0x2177), + 0x2168 => array(0x2178), + 0x2169 => array(0x2179), + 0x216A => array(0x217A), + 0x216B => array(0x217B), + 0x216C => array(0x217C), + 0x216D => array(0x217D), + 0x216E => array(0x217E), + 0x216F => array(0x217F), + 0x24B6 => array(0x24D0), + 0x24B7 => array(0x24D1), + 0x24B8 => array(0x24D2), + 0x24B9 => array(0x24D3), + 0x24BA => array(0x24D4), + 0x24BB => array(0x24D5), + 0x24BC => array(0x24D6), + 0x24BD => array(0x24D7), + 0x24BE => array(0x24D8), + 0x24BF => array(0x24D9), + 0x24C0 => array(0x24DA), + 0x24C1 => array(0x24DB), + 0x24C2 => array(0x24DC), + 0x24C3 => array(0x24DD), + 0x24C4 => array(0x24DE), + 0x24C5 => array(0x24DF), + 0x24C6 => array(0x24E0), + 0x24C7 => array(0x24E1), + 0x24C8 => array(0x24E2), + 0x24C9 => array(0x24E3), + 0x24CA => array(0x24E4), + 0x24CB => array(0x24E5), + 0x24CC => array(0x24E6), + 0x24CD => array(0x24E7), + 0x24CE => array(0x24E8), + 0x24CF => array(0x24E9), + 0x3371 => array(0x68, 0x70, 0x61), + 0x3373 => array(0x61, 0x75), + 0x3375 => array(0x6F, 0x76), + 0x3380 => array(0x70, 0x61), + 0x3381 => array(0x6E, 0x61), + 0x3382 => array(0x3BC, 0x61), + 0x3383 => array(0x6D, 0x61), + 0x3384 => array(0x6B, 0x61), + 0x3385 => array(0x6B, 0x62), + 0x3386 => array(0x6D, 0x62), + 0x3387 => array(0x67, 0x62), + 0x338A => array(0x70, 0x66), + 0x338B => array(0x6E, 0x66), + 0x338C => array(0x3BC, 0x66), + 0x3390 => array(0x68, 0x7A), + 0x3391 => array(0x6B, 0x68, 0x7A), + 0x3392 => array(0x6D, 0x68, 0x7A), + 0x3393 => array(0x67, 0x68, 0x7A), + 0x3394 => array(0x74, 0x68, 0x7A), + 0x33A9 => array(0x70, 0x61), + 0x33AA => array(0x6B, 0x70, 0x61), + 0x33AB => array(0x6D, 0x70, 0x61), + 0x33AC => array(0x67, 0x70, 0x61), + 0x33B4 => array(0x70, 0x76), + 0x33B5 => array(0x6E, 0x76), + 0x33B6 => array(0x3BC, 0x76), + 0x33B7 => array(0x6D, 0x76), + 0x33B8 => array(0x6B, 0x76), + 0x33B9 => array(0x6D, 0x76), + 0x33BA => array(0x70, 0x77), + 0x33BB => array(0x6E, 0x77), + 0x33BC => array(0x3BC, 0x77), + 0x33BD => array(0x6D, 0x77), + 0x33BE => array(0x6B, 0x77), + 0x33BF => array(0x6D, 0x77), + 0x33C0 => array(0x6B, 0x3C9), + 0x33C1 => array(0x6D, 0x3C9), /* + 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ + 0x33C3 => array(0x62, 0x71), + 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), + 0x33C7 => array(0x63, 0x6F, 0x2E), + 0x33C8 => array(0x64, 0x62), + 0x33C9 => array(0x67, 0x79), + 0x33CB => array(0x68, 0x70), + 0x33CD => array(0x6B, 0x6B), + 0x33CE => array(0x6B, 0x6D), + 0x33D7 => array(0x70, 0x68), + 0x33D9 => array(0x70, 0x70, 0x6D), + 0x33DA => array(0x70, 0x72), + 0x33DC => array(0x73, 0x76), + 0x33DD => array(0x77, 0x62), + 0xFB00 => array(0x66, 0x66), + 0xFB01 => array(0x66, 0x69), + 0xFB02 => array(0x66, 0x6C), + 0xFB03 => array(0x66, 0x66, 0x69), + 0xFB04 => array(0x66, 0x66, 0x6C), + 0xFB05 => array(0x73, 0x74), + 0xFB06 => array(0x73, 0x74), + 0xFB13 => array(0x574, 0x576), + 0xFB14 => array(0x574, 0x565), + 0xFB15 => array(0x574, 0x56B), + 0xFB16 => array(0x57E, 0x576), + 0xFB17 => array(0x574, 0x56D), + 0xFF21 => array(0xFF41), + 0xFF22 => array(0xFF42), + 0xFF23 => array(0xFF43), + 0xFF24 => array(0xFF44), + 0xFF25 => array(0xFF45), + 0xFF26 => array(0xFF46), + 0xFF27 => array(0xFF47), + 0xFF28 => array(0xFF48), + 0xFF29 => array(0xFF49), + 0xFF2A => array(0xFF4A), + 0xFF2B => array(0xFF4B), + 0xFF2C => array(0xFF4C), + 0xFF2D => array(0xFF4D), + 0xFF2E => array(0xFF4E), + 0xFF2F => array(0xFF4F), + 0xFF30 => array(0xFF50), + 0xFF31 => array(0xFF51), + 0xFF32 => array(0xFF52), + 0xFF33 => array(0xFF53), + 0xFF34 => array(0xFF54), + 0xFF35 => array(0xFF55), + 0xFF36 => array(0xFF56), + 0xFF37 => array(0xFF57), + 0xFF38 => array(0xFF58), + 0xFF39 => array(0xFF59), + 0xFF3A => array(0xFF5A), + 0x10400 => array(0x10428), + 0x10401 => array(0x10429), + 0x10402 => array(0x1042A), + 0x10403 => array(0x1042B), + 0x10404 => array(0x1042C), + 0x10405 => array(0x1042D), + 0x10406 => array(0x1042E), + 0x10407 => array(0x1042F), + 0x10408 => array(0x10430), + 0x10409 => array(0x10431), + 0x1040A => array(0x10432), + 0x1040B => array(0x10433), + 0x1040C => array(0x10434), + 0x1040D => array(0x10435), + 0x1040E => array(0x10436), + 0x1040F => array(0x10437), + 0x10410 => array(0x10438), + 0x10411 => array(0x10439), + 0x10412 => array(0x1043A), + 0x10413 => array(0x1043B), + 0x10414 => array(0x1043C), + 0x10415 => array(0x1043D), + 0x10416 => array(0x1043E), + 0x10417 => array(0x1043F), + 0x10418 => array(0x10440), + 0x10419 => array(0x10441), + 0x1041A => array(0x10442), + 0x1041B => array(0x10443), + 0x1041C => array(0x10444), + 0x1041D => array(0x10445), + 0x1041E => array(0x10446), + 0x1041F => array(0x10447), + 0x10420 => array(0x10448), + 0x10421 => array(0x10449), + 0x10422 => array(0x1044A), + 0x10423 => array(0x1044B), + 0x10424 => array(0x1044C), + 0x10425 => array(0x1044D), + 0x1D400 => array(0x61), + 0x1D401 => array(0x62), + 0x1D402 => array(0x63), + 0x1D403 => array(0x64), + 0x1D404 => array(0x65), + 0x1D405 => array(0x66), + 0x1D406 => array(0x67), + 0x1D407 => array(0x68), + 0x1D408 => array(0x69), + 0x1D409 => array(0x6A), + 0x1D40A => array(0x6B), + 0x1D40B => array(0x6C), + 0x1D40C => array(0x6D), + 0x1D40D => array(0x6E), + 0x1D40E => array(0x6F), + 0x1D40F => array(0x70), + 0x1D410 => array(0x71), + 0x1D411 => array(0x72), + 0x1D412 => array(0x73), + 0x1D413 => array(0x74), + 0x1D414 => array(0x75), + 0x1D415 => array(0x76), + 0x1D416 => array(0x77), + 0x1D417 => array(0x78), + 0x1D418 => array(0x79), + 0x1D419 => array(0x7A), + 0x1D434 => array(0x61), + 0x1D435 => array(0x62), + 0x1D436 => array(0x63), + 0x1D437 => array(0x64), + 0x1D438 => array(0x65), + 0x1D439 => array(0x66), + 0x1D43A => array(0x67), + 0x1D43B => array(0x68), + 0x1D43C => array(0x69), + 0x1D43D => array(0x6A), + 0x1D43E => array(0x6B), + 0x1D43F => array(0x6C), + 0x1D440 => array(0x6D), + 0x1D441 => array(0x6E), + 0x1D442 => array(0x6F), + 0x1D443 => array(0x70), + 0x1D444 => array(0x71), + 0x1D445 => array(0x72), + 0x1D446 => array(0x73), + 0x1D447 => array(0x74), + 0x1D448 => array(0x75), + 0x1D449 => array(0x76), + 0x1D44A => array(0x77), + 0x1D44B => array(0x78), + 0x1D44C => array(0x79), + 0x1D44D => array(0x7A), + 0x1D468 => array(0x61), + 0x1D469 => array(0x62), + 0x1D46A => array(0x63), + 0x1D46B => array(0x64), + 0x1D46C => array(0x65), + 0x1D46D => array(0x66), + 0x1D46E => array(0x67), + 0x1D46F => array(0x68), + 0x1D470 => array(0x69), + 0x1D471 => array(0x6A), + 0x1D472 => array(0x6B), + 0x1D473 => array(0x6C), + 0x1D474 => array(0x6D), + 0x1D475 => array(0x6E), + 0x1D476 => array(0x6F), + 0x1D477 => array(0x70), + 0x1D478 => array(0x71), + 0x1D479 => array(0x72), + 0x1D47A => array(0x73), + 0x1D47B => array(0x74), + 0x1D47C => array(0x75), + 0x1D47D => array(0x76), + 0x1D47E => array(0x77), + 0x1D47F => array(0x78), + 0x1D480 => array(0x79), + 0x1D481 => array(0x7A), + 0x1D49C => array(0x61), + 0x1D49E => array(0x63), + 0x1D49F => array(0x64), + 0x1D4A2 => array(0x67), + 0x1D4A5 => array(0x6A), + 0x1D4A6 => array(0x6B), + 0x1D4A9 => array(0x6E), + 0x1D4AA => array(0x6F), + 0x1D4AB => array(0x70), + 0x1D4AC => array(0x71), + 0x1D4AE => array(0x73), + 0x1D4AF => array(0x74), + 0x1D4B0 => array(0x75), + 0x1D4B1 => array(0x76), + 0x1D4B2 => array(0x77), + 0x1D4B3 => array(0x78), + 0x1D4B4 => array(0x79), + 0x1D4B5 => array(0x7A), + 0x1D4D0 => array(0x61), + 0x1D4D1 => array(0x62), + 0x1D4D2 => array(0x63), + 0x1D4D3 => array(0x64), + 0x1D4D4 => array(0x65), + 0x1D4D5 => array(0x66), + 0x1D4D6 => array(0x67), + 0x1D4D7 => array(0x68), + 0x1D4D8 => array(0x69), + 0x1D4D9 => array(0x6A), + 0x1D4DA => array(0x6B), + 0x1D4DB => array(0x6C), + 0x1D4DC => array(0x6D), + 0x1D4DD => array(0x6E), + 0x1D4DE => array(0x6F), + 0x1D4DF => array(0x70), + 0x1D4E0 => array(0x71), + 0x1D4E1 => array(0x72), + 0x1D4E2 => array(0x73), + 0x1D4E3 => array(0x74), + 0x1D4E4 => array(0x75), + 0x1D4E5 => array(0x76), + 0x1D4E6 => array(0x77), + 0x1D4E7 => array(0x78), + 0x1D4E8 => array(0x79), + 0x1D4E9 => array(0x7A), + 0x1D504 => array(0x61), + 0x1D505 => array(0x62), + 0x1D507 => array(0x64), + 0x1D508 => array(0x65), + 0x1D509 => array(0x66), + 0x1D50A => array(0x67), + 0x1D50D => array(0x6A), + 0x1D50E => array(0x6B), + 0x1D50F => array(0x6C), + 0x1D510 => array(0x6D), + 0x1D511 => array(0x6E), + 0x1D512 => array(0x6F), + 0x1D513 => array(0x70), + 0x1D514 => array(0x71), + 0x1D516 => array(0x73), + 0x1D517 => array(0x74), + 0x1D518 => array(0x75), + 0x1D519 => array(0x76), + 0x1D51A => array(0x77), + 0x1D51B => array(0x78), + 0x1D51C => array(0x79), + 0x1D538 => array(0x61), + 0x1D539 => array(0x62), + 0x1D53B => array(0x64), + 0x1D53C => array(0x65), + 0x1D53D => array(0x66), + 0x1D53E => array(0x67), + 0x1D540 => array(0x69), + 0x1D541 => array(0x6A), + 0x1D542 => array(0x6B), + 0x1D543 => array(0x6C), + 0x1D544 => array(0x6D), + 0x1D546 => array(0x6F), + 0x1D54A => array(0x73), + 0x1D54B => array(0x74), + 0x1D54C => array(0x75), + 0x1D54D => array(0x76), + 0x1D54E => array(0x77), + 0x1D54F => array(0x78), + 0x1D550 => array(0x79), + 0x1D56C => array(0x61), + 0x1D56D => array(0x62), + 0x1D56E => array(0x63), + 0x1D56F => array(0x64), + 0x1D570 => array(0x65), + 0x1D571 => array(0x66), + 0x1D572 => array(0x67), + 0x1D573 => array(0x68), + 0x1D574 => array(0x69), + 0x1D575 => array(0x6A), + 0x1D576 => array(0x6B), + 0x1D577 => array(0x6C), + 0x1D578 => array(0x6D), + 0x1D579 => array(0x6E), + 0x1D57A => array(0x6F), + 0x1D57B => array(0x70), + 0x1D57C => array(0x71), + 0x1D57D => array(0x72), + 0x1D57E => array(0x73), + 0x1D57F => array(0x74), + 0x1D580 => array(0x75), + 0x1D581 => array(0x76), + 0x1D582 => array(0x77), + 0x1D583 => array(0x78), + 0x1D584 => array(0x79), + 0x1D585 => array(0x7A), + 0x1D5A0 => array(0x61), + 0x1D5A1 => array(0x62), + 0x1D5A2 => array(0x63), + 0x1D5A3 => array(0x64), + 0x1D5A4 => array(0x65), + 0x1D5A5 => array(0x66), + 0x1D5A6 => array(0x67), + 0x1D5A7 => array(0x68), + 0x1D5A8 => array(0x69), + 0x1D5A9 => array(0x6A), + 0x1D5AA => array(0x6B), + 0x1D5AB => array(0x6C), + 0x1D5AC => array(0x6D), + 0x1D5AD => array(0x6E), + 0x1D5AE => array(0x6F), + 0x1D5AF => array(0x70), + 0x1D5B0 => array(0x71), + 0x1D5B1 => array(0x72), + 0x1D5B2 => array(0x73), + 0x1D5B3 => array(0x74), + 0x1D5B4 => array(0x75), + 0x1D5B5 => array(0x76), + 0x1D5B6 => array(0x77), + 0x1D5B7 => array(0x78), + 0x1D5B8 => array(0x79), + 0x1D5B9 => array(0x7A), + 0x1D5D4 => array(0x61), + 0x1D5D5 => array(0x62), + 0x1D5D6 => array(0x63), + 0x1D5D7 => array(0x64), + 0x1D5D8 => array(0x65), + 0x1D5D9 => array(0x66), + 0x1D5DA => array(0x67), + 0x1D5DB => array(0x68), + 0x1D5DC => array(0x69), + 0x1D5DD => array(0x6A), + 0x1D5DE => array(0x6B), + 0x1D5DF => array(0x6C), + 0x1D5E0 => array(0x6D), + 0x1D5E1 => array(0x6E), + 0x1D5E2 => array(0x6F), + 0x1D5E3 => array(0x70), + 0x1D5E4 => array(0x71), + 0x1D5E5 => array(0x72), + 0x1D5E6 => array(0x73), + 0x1D5E7 => array(0x74), + 0x1D5E8 => array(0x75), + 0x1D5E9 => array(0x76), + 0x1D5EA => array(0x77), + 0x1D5EB => array(0x78), + 0x1D5EC => array(0x79), + 0x1D5ED => array(0x7A), + 0x1D608 => array(0x61), + 0x1D609 => array(0x62), + 0x1D60A => array(0x63), + 0x1D60B => array(0x64), + 0x1D60C => array(0x65), + 0x1D60D => array(0x66), + 0x1D60E => array(0x67), + 0x1D60F => array(0x68), + 0x1D610 => array(0x69), + 0x1D611 => array(0x6A), + 0x1D612 => array(0x6B), + 0x1D613 => array(0x6C), + 0x1D614 => array(0x6D), + 0x1D615 => array(0x6E), + 0x1D616 => array(0x6F), + 0x1D617 => array(0x70), + 0x1D618 => array(0x71), + 0x1D619 => array(0x72), + 0x1D61A => array(0x73), + 0x1D61B => array(0x74), + 0x1D61C => array(0x75), + 0x1D61D => array(0x76), + 0x1D61E => array(0x77), + 0x1D61F => array(0x78), + 0x1D620 => array(0x79), + 0x1D621 => array(0x7A), + 0x1D63C => array(0x61), + 0x1D63D => array(0x62), + 0x1D63E => array(0x63), + 0x1D63F => array(0x64), + 0x1D640 => array(0x65), + 0x1D641 => array(0x66), + 0x1D642 => array(0x67), + 0x1D643 => array(0x68), + 0x1D644 => array(0x69), + 0x1D645 => array(0x6A), + 0x1D646 => array(0x6B), + 0x1D647 => array(0x6C), + 0x1D648 => array(0x6D), + 0x1D649 => array(0x6E), + 0x1D64A => array(0x6F), + 0x1D64B => array(0x70), + 0x1D64C => array(0x71), + 0x1D64D => array(0x72), + 0x1D64E => array(0x73), + 0x1D64F => array(0x74), + 0x1D650 => array(0x75), + 0x1D651 => array(0x76), + 0x1D652 => array(0x77), + 0x1D653 => array(0x78), + 0x1D654 => array(0x79), + 0x1D655 => array(0x7A), + 0x1D670 => array(0x61), + 0x1D671 => array(0x62), + 0x1D672 => array(0x63), + 0x1D673 => array(0x64), + 0x1D674 => array(0x65), + 0x1D675 => array(0x66), + 0x1D676 => array(0x67), + 0x1D677 => array(0x68), + 0x1D678 => array(0x69), + 0x1D679 => array(0x6A), + 0x1D67A => array(0x6B), + 0x1D67B => array(0x6C), + 0x1D67C => array(0x6D), + 0x1D67D => array(0x6E), + 0x1D67E => array(0x6F), + 0x1D67F => array(0x70), + 0x1D680 => array(0x71), + 0x1D681 => array(0x72), + 0x1D682 => array(0x73), + 0x1D683 => array(0x74), + 0x1D684 => array(0x75), + 0x1D685 => array(0x76), + 0x1D686 => array(0x77), + 0x1D687 => array(0x78), + 0x1D688 => array(0x79), + 0x1D689 => array(0x7A), + 0x1D6A8 => array(0x3B1), + 0x1D6A9 => array(0x3B2), + 0x1D6AA => array(0x3B3), + 0x1D6AB => array(0x3B4), + 0x1D6AC => array(0x3B5), + 0x1D6AD => array(0x3B6), + 0x1D6AE => array(0x3B7), + 0x1D6AF => array(0x3B8), + 0x1D6B0 => array(0x3B9), + 0x1D6B1 => array(0x3BA), + 0x1D6B2 => array(0x3BB), + 0x1D6B3 => array(0x3BC), + 0x1D6B4 => array(0x3BD), + 0x1D6B5 => array(0x3BE), + 0x1D6B6 => array(0x3BF), + 0x1D6B7 => array(0x3C0), + 0x1D6B8 => array(0x3C1), + 0x1D6B9 => array(0x3B8), + 0x1D6BA => array(0x3C3), + 0x1D6BB => array(0x3C4), + 0x1D6BC => array(0x3C5), + 0x1D6BD => array(0x3C6), + 0x1D6BE => array(0x3C7), + 0x1D6BF => array(0x3C8), + 0x1D6C0 => array(0x3C9), + 0x1D6D3 => array(0x3C3), + 0x1D6E2 => array(0x3B1), + 0x1D6E3 => array(0x3B2), + 0x1D6E4 => array(0x3B3), + 0x1D6E5 => array(0x3B4), + 0x1D6E6 => array(0x3B5), + 0x1D6E7 => array(0x3B6), + 0x1D6E8 => array(0x3B7), + 0x1D6E9 => array(0x3B8), + 0x1D6EA => array(0x3B9), + 0x1D6EB => array(0x3BA), + 0x1D6EC => array(0x3BB), + 0x1D6ED => array(0x3BC), + 0x1D6EE => array(0x3BD), + 0x1D6EF => array(0x3BE), + 0x1D6F0 => array(0x3BF), + 0x1D6F1 => array(0x3C0), + 0x1D6F2 => array(0x3C1), + 0x1D6F3 => array(0x3B8), + 0x1D6F4 => array(0x3C3), + 0x1D6F5 => array(0x3C4), + 0x1D6F6 => array(0x3C5), + 0x1D6F7 => array(0x3C6), + 0x1D6F8 => array(0x3C7), + 0x1D6F9 => array(0x3C8), + 0x1D6FA => array(0x3C9), + 0x1D70D => array(0x3C3), + 0x1D71C => array(0x3B1), + 0x1D71D => array(0x3B2), + 0x1D71E => array(0x3B3), + 0x1D71F => array(0x3B4), + 0x1D720 => array(0x3B5), + 0x1D721 => array(0x3B6), + 0x1D722 => array(0x3B7), + 0x1D723 => array(0x3B8), + 0x1D724 => array(0x3B9), + 0x1D725 => array(0x3BA), + 0x1D726 => array(0x3BB), + 0x1D727 => array(0x3BC), + 0x1D728 => array(0x3BD), + 0x1D729 => array(0x3BE), + 0x1D72A => array(0x3BF), + 0x1D72B => array(0x3C0), + 0x1D72C => array(0x3C1), + 0x1D72D => array(0x3B8), + 0x1D72E => array(0x3C3), + 0x1D72F => array(0x3C4), + 0x1D730 => array(0x3C5), + 0x1D731 => array(0x3C6), + 0x1D732 => array(0x3C7), + 0x1D733 => array(0x3C8), + 0x1D734 => array(0x3C9), + 0x1D747 => array(0x3C3), + 0x1D756 => array(0x3B1), + 0x1D757 => array(0x3B2), + 0x1D758 => array(0x3B3), + 0x1D759 => array(0x3B4), + 0x1D75A => array(0x3B5), + 0x1D75B => array(0x3B6), + 0x1D75C => array(0x3B7), + 0x1D75D => array(0x3B8), + 0x1D75E => array(0x3B9), + 0x1D75F => array(0x3BA), + 0x1D760 => array(0x3BB), + 0x1D761 => array(0x3BC), + 0x1D762 => array(0x3BD), + 0x1D763 => array(0x3BE), + 0x1D764 => array(0x3BF), + 0x1D765 => array(0x3C0), + 0x1D766 => array(0x3C1), + 0x1D767 => array(0x3B8), + 0x1D768 => array(0x3C3), + 0x1D769 => array(0x3C4), + 0x1D76A => array(0x3C5), + 0x1D76B => array(0x3C6), + 0x1D76C => array(0x3C7), + 0x1D76D => array(0x3C8), + 0x1D76E => array(0x3C9), + 0x1D781 => array(0x3C3), + 0x1D790 => array(0x3B1), + 0x1D791 => array(0x3B2), + 0x1D792 => array(0x3B3), + 0x1D793 => array(0x3B4), + 0x1D794 => array(0x3B5), + 0x1D795 => array(0x3B6), + 0x1D796 => array(0x3B7), + 0x1D797 => array(0x3B8), + 0x1D798 => array(0x3B9), + 0x1D799 => array(0x3BA), + 0x1D79A => array(0x3BB), + 0x1D79B => array(0x3BC), + 0x1D79C => array(0x3BD), + 0x1D79D => array(0x3BE), + 0x1D79E => array(0x3BF), + 0x1D79F => array(0x3C0), + 0x1D7A0 => array(0x3C1), + 0x1D7A1 => array(0x3B8), + 0x1D7A2 => array(0x3C3), + 0x1D7A3 => array(0x3C4), + 0x1D7A4 => array(0x3C5), + 0x1D7A5 => array(0x3C6), + 0x1D7A6 => array(0x3C7), + 0x1D7A7 => array(0x3C8), + 0x1D7A8 => array(0x3C9), + 0x1D7BB => array(0x3C3), + 0x3F9 => array(0x3C3), + 0x1D2C => array(0x61), + 0x1D2D => array(0xE6), + 0x1D2E => array(0x62), + 0x1D30 => array(0x64), + 0x1D31 => array(0x65), + 0x1D32 => array(0x1DD), + 0x1D33 => array(0x67), + 0x1D34 => array(0x68), + 0x1D35 => array(0x69), + 0x1D36 => array(0x6A), + 0x1D37 => array(0x6B), + 0x1D38 => array(0x6C), + 0x1D39 => array(0x6D), + 0x1D3A => array(0x6E), + 0x1D3C => array(0x6F), + 0x1D3D => array(0x223), + 0x1D3E => array(0x70), + 0x1D3F => array(0x72), + 0x1D40 => array(0x74), + 0x1D41 => array(0x75), + 0x1D42 => array(0x77), + 0x213B => array(0x66, 0x61, 0x78), + 0x3250 => array(0x70, 0x74, 0x65), + 0x32CC => array(0x68, 0x67), + 0x32CE => array(0x65, 0x76), + 0x32CF => array(0x6C, 0x74, 0x64), + 0x337A => array(0x69, 0x75), + 0x33DE => array(0x76, 0x2215, 0x6D), + 0x33DF => array(0x61, 0x2215, 0x6D) + ); + + /** + * Normalization Combining Classes; Code Points not listed + * got Combining Class 0. + * + * @static + * @var array + * @access private + */ + var $_np_norm_combcls = array( + 0x334 => 1, + 0x335 => 1, + 0x336 => 1, + 0x337 => 1, + 0x338 => 1, + 0x93C => 7, + 0x9BC => 7, + 0xA3C => 7, + 0xABC => 7, + 0xB3C => 7, + 0xCBC => 7, + 0x1037 => 7, + 0x3099 => 8, + 0x309A => 8, + 0x94D => 9, + 0x9CD => 9, + 0xA4D => 9, + 0xACD => 9, + 0xB4D => 9, + 0xBCD => 9, + 0xC4D => 9, + 0xCCD => 9, + 0xD4D => 9, + 0xDCA => 9, + 0xE3A => 9, + 0xF84 => 9, + 0x1039 => 9, + 0x1714 => 9, + 0x1734 => 9, + 0x17D2 => 9, + 0x5B0 => 10, + 0x5B1 => 11, + 0x5B2 => 12, + 0x5B3 => 13, + 0x5B4 => 14, + 0x5B5 => 15, + 0x5B6 => 16, + 0x5B7 => 17, + 0x5B8 => 18, + 0x5B9 => 19, + 0x5BB => 20, + 0x5Bc => 21, + 0x5BD => 22, + 0x5BF => 23, + 0x5C1 => 24, + 0x5C2 => 25, + 0xFB1E => 26, + 0x64B => 27, + 0x64C => 28, + 0x64D => 29, + 0x64E => 30, + 0x64F => 31, + 0x650 => 32, + 0x651 => 33, + 0x652 => 34, + 0x670 => 35, + 0x711 => 36, + 0xC55 => 84, + 0xC56 => 91, + 0xE38 => 103, + 0xE39 => 103, + 0xE48 => 107, + 0xE49 => 107, + 0xE4A => 107, + 0xE4B => 107, + 0xEB8 => 118, + 0xEB9 => 118, + 0xEC8 => 122, + 0xEC9 => 122, + 0xECA => 122, + 0xECB => 122, + 0xF71 => 129, + 0xF72 => 130, + 0xF7A => 130, + 0xF7B => 130, + 0xF7C => 130, + 0xF7D => 130, + 0xF80 => 130, + 0xF74 => 132, + 0x321 => 202, + 0x322 => 202, + 0x327 => 202, + 0x328 => 202, + 0x31B => 216, + 0xF39 => 216, + 0x1D165 => 216, + 0x1D166 => 216, + 0x1D16E => 216, + 0x1D16F => 216, + 0x1D170 => 216, + 0x1D171 => 216, + 0x1D172 => 216, + 0x302A => 218, + 0x316 => 220, + 0x317 => 220, + 0x318 => 220, + 0x319 => 220, + 0x31C => 220, + 0x31D => 220, + 0x31E => 220, + 0x31F => 220, + 0x320 => 220, + 0x323 => 220, + 0x324 => 220, + 0x325 => 220, + 0x326 => 220, + 0x329 => 220, + 0x32A => 220, + 0x32B => 220, + 0x32C => 220, + 0x32D => 220, + 0x32E => 220, + 0x32F => 220, + 0x330 => 220, + 0x331 => 220, + 0x332 => 220, + 0x333 => 220, + 0x339 => 220, + 0x33A => 220, + 0x33B => 220, + 0x33C => 220, + 0x347 => 220, + 0x348 => 220, + 0x349 => 220, + 0x34D => 220, + 0x34E => 220, + 0x353 => 220, + 0x354 => 220, + 0x355 => 220, + 0x356 => 220, + 0x591 => 220, + 0x596 => 220, + 0x59B => 220, + 0x5A3 => 220, + 0x5A4 => 220, + 0x5A5 => 220, + 0x5A6 => 220, + 0x5A7 => 220, + 0x5AA => 220, + 0x655 => 220, + 0x656 => 220, + 0x6E3 => 220, + 0x6EA => 220, + 0x6ED => 220, + 0x731 => 220, + 0x734 => 220, + 0x737 => 220, + 0x738 => 220, + 0x739 => 220, + 0x73B => 220, + 0x73C => 220, + 0x73E => 220, + 0x742 => 220, + 0x744 => 220, + 0x746 => 220, + 0x748 => 220, + 0x952 => 220, + 0xF18 => 220, + 0xF19 => 220, + 0xF35 => 220, + 0xF37 => 220, + 0xFC6 => 220, + 0x193B => 220, + 0x20E8 => 220, + 0x1D17B => 220, + 0x1D17C => 220, + 0x1D17D => 220, + 0x1D17E => 220, + 0x1D17F => 220, + 0x1D180 => 220, + 0x1D181 => 220, + 0x1D182 => 220, + 0x1D18A => 220, + 0x1D18B => 220, + 0x59A => 222, + 0x5AD => 222, + 0x1929 => 222, + 0x302D => 222, + 0x302E => 224, + 0x302F => 224, + 0x1D16D => 226, + 0x5AE => 228, + 0x18A9 => 228, + 0x302B => 228, + 0x300 => 230, + 0x301 => 230, + 0x302 => 230, + 0x303 => 230, + 0x304 => 230, + 0x305 => 230, + 0x306 => 230, + 0x307 => 230, + 0x308 => 230, + 0x309 => 230, + 0x30A => 230, + 0x30B => 230, + 0x30C => 230, + 0x30D => 230, + 0x30E => 230, + 0x30F => 230, + 0x310 => 230, + 0x311 => 230, + 0x312 => 230, + 0x313 => 230, + 0x314 => 230, + 0x33D => 230, + 0x33E => 230, + 0x33F => 230, + 0x340 => 230, + 0x341 => 230, + 0x342 => 230, + 0x343 => 230, + 0x344 => 230, + 0x346 => 230, + 0x34A => 230, + 0x34B => 230, + 0x34C => 230, + 0x350 => 230, + 0x351 => 230, + 0x352 => 230, + 0x357 => 230, + 0x363 => 230, + 0x364 => 230, + 0x365 => 230, + 0x366 => 230, + 0x367 => 230, + 0x368 => 230, + 0x369 => 230, + 0x36A => 230, + 0x36B => 230, + 0x36C => 230, + 0x36D => 230, + 0x36E => 230, + 0x36F => 230, + 0x483 => 230, + 0x484 => 230, + 0x485 => 230, + 0x486 => 230, + 0x592 => 230, + 0x593 => 230, + 0x594 => 230, + 0x595 => 230, + 0x597 => 230, + 0x598 => 230, + 0x599 => 230, + 0x59C => 230, + 0x59D => 230, + 0x59E => 230, + 0x59F => 230, + 0x5A0 => 230, + 0x5A1 => 230, + 0x5A8 => 230, + 0x5A9 => 230, + 0x5AB => 230, + 0x5AC => 230, + 0x5AF => 230, + 0x5C4 => 230, + 0x610 => 230, + 0x611 => 230, + 0x612 => 230, + 0x613 => 230, + 0x614 => 230, + 0x615 => 230, + 0x653 => 230, + 0x654 => 230, + 0x657 => 230, + 0x658 => 230, + 0x6D6 => 230, + 0x6D7 => 230, + 0x6D8 => 230, + 0x6D9 => 230, + 0x6DA => 230, + 0x6DB => 230, + 0x6DC => 230, + 0x6DF => 230, + 0x6E0 => 230, + 0x6E1 => 230, + 0x6E2 => 230, + 0x6E4 => 230, + 0x6E7 => 230, + 0x6E8 => 230, + 0x6EB => 230, + 0x6EC => 230, + 0x730 => 230, + 0x732 => 230, + 0x733 => 230, + 0x735 => 230, + 0x736 => 230, + 0x73A => 230, + 0x73D => 230, + 0x73F => 230, + 0x740 => 230, + 0x741 => 230, + 0x743 => 230, + 0x745 => 230, + 0x747 => 230, + 0x749 => 230, + 0x74A => 230, + 0x951 => 230, + 0x953 => 230, + 0x954 => 230, + 0xF82 => 230, + 0xF83 => 230, + 0xF86 => 230, + 0xF87 => 230, + 0x170D => 230, + 0x193A => 230, + 0x20D0 => 230, + 0x20D1 => 230, + 0x20D4 => 230, + 0x20D5 => 230, + 0x20D6 => 230, + 0x20D7 => 230, + 0x20DB => 230, + 0x20DC => 230, + 0x20E1 => 230, + 0x20E7 => 230, + 0x20E9 => 230, + 0xFE20 => 230, + 0xFE21 => 230, + 0xFE22 => 230, + 0xFE23 => 230, + 0x1D185 => 230, + 0x1D186 => 230, + 0x1D187 => 230, + 0x1D189 => 230, + 0x1D188 => 230, + 0x1D1AA => 230, + 0x1D1AB => 230, + 0x1D1AC => 230, + 0x1D1AD => 230, + 0x315 => 232, + 0x31A => 232, + 0x302C => 232, + 0x35F => 233, + 0x362 => 233, + 0x35D => 234, + 0x35E => 234, + 0x360 => 234, + 0x361 => 234, + 0x345 => 240 + ); + // }}} + + + // Internal settings, do not mess with them + var $_punycode_prefix = 'xn--'; + var $_invalid_ucs = 0x80000000; + var $_max_ucs = 0x10FFFF; + var $_base = 36; + var $_tmin = 1; + var $_tmax = 26; + var $_skew = 38; + var $_damp = 700; + var $_initial_bias = 72; + var $_initial_n = 0x80; + var $_sbase = 0xAC00; + var $_lbase = 0x1100; + var $_vbase = 0x1161; + var $_tbase = 0x11a7; + var $_lcount = 19; + var $_vcount = 21; + var $_tcount = 28; + var $_ncount = 588; // _vcount * _tcount + var $_scount = 11172; // _lcount * _tcount * _vcount + var $_error = false; + + // See set_parameter() for details of how to change the following settings + // from within your script / application + var $_api_encoding = 'utf8'; // Default input charset is UTF-8 + var $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden + var $_strict_mode = false; // Behave strict or not + + // The constructor + function Net_IDNA_php4($options = false) + { + $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; + // If parameters are given, pass these to the respective method + if (is_array($options)) { + return $this->set_parameter($options); + } + return true; + } + + /** + * Sets a new option value. Available options and values: + * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, + * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] + * [overlong - Unicode does not allow unnecessarily long encodings of chars, + * to allow this, set this parameter to true, else to false; + * default is false.] + * [strict - true: strict mode, good for registration purposes - Causes errors + * on failures; false: loose mode, ideal for "wildlife" applications + * by silently ignoring errors and returning the original input instead + * + * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) + * @param string Value to use (if parameter 1 is a string) + * @return boolean true on success, false otherwise + * @access public + */ + function set_parameter($option, $value = false) + { + if (!is_array($option)) { + $option = array($option => $value); + } + foreach ($option as $k => $v) { + switch ($k) { + case 'encoding': + switch ($v) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + $this->_api_encoding = $v; + break; + default: + $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k); + return false; + } + break; + case 'overlong': + $this->_allow_overlong = ($v) ? true : false; + break; + case 'strict': + $this->_strict_mode = ($v) ? true : false; + break; + default: + $this->_error('Set Parameter: Unknown option '.$k); + return false; + } + } + return true; + } + + /** + * Decode a given ACE domain name + * @param string Domain name (ACE string) + * [@param string Desired output encoding, see {@link set_parameter}] + * @return string Decoded Domain name (UTF-8 or UCS-4) + * @access public + */ + function decode($input, $one_time_encoding = false) + { + // Optionally set + if ($one_time_encoding) { + switch ($one_time_encoding) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + break; + default: + $this->_error('Unknown encoding '.$one_time_encoding); + return false; + } + } + // Make sure to drop any newline characters around + $input = trim($input); + + // Negotiate input and try to determine, wether it is a plain string, + // an email address or something like a complete URL + if (strpos($input, '@')) { // Maybe it is an email address + // No no in strict mode + if ($this->_strict_mode) { + $this->_error('Only simple domain name parts can be handled in strict mode'); + return false; + } + list($email_pref, $input) = explode('@', $input, 2); + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $return = $email_pref . '@' . join('.', $arr); + } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) + // No no in strict mode + if ($this->_strict_mode) { + $this->_error('Only simple domain name parts can be handled in strict mode'); + return false; + } + $parsed = parse_url($input); + if (isset($parsed['host'])) { + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $parsed['host'] = join('.', $arr); + if (isset($parsed['scheme'])) { + $parsed['scheme'] .= (strtolower($parsed['scheme']) == 'mailto') ? ':' : '://'; + } + $return = join('', $parsed); + } else { // parse_url seems to have failed, try without it + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $return = join('.', $arr); + } + } else { // Otherwise we consider it being a pure domain name string + $return = $this->_decode($input); + } + // The output is UTF-8 by default, other output formats need conversion here + // If one time encoding is given, use this, else the objects property + switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + return $return; + break; + case 'ucs4_string': + return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); + break; + case 'ucs4_array': + return $this->_utf8_to_ucs4($return); + break; + default: + $this->_error('Unsupported output format'); + return false; + } + } + + /** + * Encode a given UTF-8 domain name + * @param string Domain name (UTF-8 or UCS-4) + * [@param string Desired input encoding, see {@link set_parameter}] + * @return string Encoded Domain name (ACE string) + * @access public + */ + function encode($decoded, $one_time_encoding = false) + { + // Forcing conversion of input to UCS4 array + // If one time encoding is given, use this, else the objects property + switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + $decoded = $this->_utf8_to_ucs4($decoded); + break; + case 'ucs4_string': + $decoded = $this->_ucs4_string_to_ucs4($decoded); + case 'ucs4_array': + break; + default: + // $this->_error('Unsupported input format: '.$this->_api_encoding); + $this->_error('Unsupported input format'); + return false; + } + + // No input, no output, what else did you expect? + if (empty($decoded)) return ''; + + // Anchors for iteration + $last_begin = 0; + // Output string + $output = ''; + foreach ($decoded as $k => $v) { + // Make sure to use just the plain dot + switch($v) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + $decoded[$k] = 0x2E; + // It's right, no break here + // The codepoints above have to be converted to dots anyway + + // Stumbling across an anchoring character + case 0x2E: + case 0x2F: + case 0x3A: + case 0x3F: + case 0x40: + // Neither email addresses nor URLs allowed in strict mode + if ($this->_strict_mode) { + $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); + return false; + } else { + // Skip first char + if ($k) { + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); + } + $output .= chr($decoded[$k]); + } + $last_begin = $k + 1; + } + } + } + // Catch the rest of the string + if ($last_begin) { + $inp_len = sizeof($decoded); + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); + } + return $output; + } else { + if ($output = $this->_encode($decoded)) { + return $output; + } else { + return $this->_ucs4_to_utf8($decoded); + } + } + } + + /** + * Use this method to get the last error ocurred + * @param void + * @return string The last error, that occured + * @access public + */ + function get_last_error() + { + return $this->_error; + } + + /** + * The actual decoding algorithm + * @access private + */ + function _decode($encoded) + { + // We do need to find the Punycode prefix + if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) { + $this->_error('This is not a punycode string'); + return false; + } + $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded); + // If nothing left after removing the prefix, it is hopeless + if (!$encode_test) { + $this->_error('The given encoded string was empty'); + return false; + } + // Find last occurence of the delimiter + $delim_pos = strrpos($encoded, '-'); + if ($delim_pos > strlen($this->_punycode_prefix)) { + for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) { + $decoded[] = ord($encoded{$k}); + } + } else { + $decoded = array(); + } + $deco_len = count($decoded); + $enco_len = strlen($encoded); + + // Wandering through the strings; init + $is_first = true; + $bias = $this->_initial_bias; + $idx = 0; + $char = $this->_initial_n; + + for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { + for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { + $digit = $this->_decode_digit($encoded{$enco_idx++}); + $idx += $digit * $w; + $t = ($k <= $bias) ? $this->_tmin : + (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); + if ($digit < $t) break; + $w = (int) ($w * ($this->_base - $t)); + } + $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); + $is_first = false; + $char += (int) ($idx / ($deco_len + 1)); + $idx %= ($deco_len + 1); + if ($deco_len > 0) { + // Make room for the decoded char + for ($i = $deco_len; $i > $idx; $i--) { + $decoded[$i] = $decoded[($i - 1)]; + } + } + $decoded[$idx++] = $char; + } + return $this->_ucs4_to_utf8($decoded); + } + + /** + * The actual encoding algorithm + * @access private + */ + function _encode($decoded) + { + // We cannot encode a domain name containing the Punycode prefix + $extract = strlen($this->_punycode_prefix); + $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); + $check_deco = array_slice($decoded, 0, $extract); + + if ($check_pref == $check_deco) { + $this->_error('This is already a punycode string'); + return false; + } + // We will not try to encode strings consisting of basic code points only + $encodable = false; + foreach ($decoded as $k => $v) { + if ($v > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + $this->_error('The given string does not contain encodable chars'); + return false; + } + + // Do NAMEPREP + $decoded = $this->_nameprep($decoded); + if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed + + $deco_len = count($decoded); + if (!$deco_len) return false; // Empty array + + $codecount = 0; // How many chars have been consumed + + $encoded = ''; + // Copy all basic code points to output + for ($i = 0; $i < $deco_len; ++$i) { + $test = $decoded[$i]; + // Will match [0-9a-zA-Z-] + if ((0x2F < $test && $test < 0x40) + || (0x40 < $test && $test < 0x5B) + || (0x60 < $test && $test <= 0x7B) + || (0x2D == $test)) { + $encoded .= chr($decoded[$i]); + $codecount++; + } + } + + // All codepoints were basic ones + if ($codecount == $deco_len) { + return $encoded; + } + + // Start with the prefix; copy it to output + $encoded = $this->_punycode_prefix.$encoded; + + // If we have basic code points in output, add an hyphen to the end + if ($codecount) $encoded .= '-'; + + // Now find and encode all non-basic code points + $is_first = true; + $cur_code = $this->_initial_n; + $bias = $this->_initial_bias; + $delta = 0; + while ($codecount < $deco_len) { + // Find the smallest code point >= the current code point and + // remember the last ouccrence of it in the input + for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { + if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { + $next_code = $decoded[$i]; + } + } + + $delta += ($next_code - $cur_code) * ($codecount + 1); + $cur_code = $next_code; + + // Scan input again and encode all characters whose code point is $cur_code + for ($i = 0; $i < $deco_len; $i++) { + if ($decoded[$i] < $cur_code) { + $delta++; + } elseif ($decoded[$i] == $cur_code) { + for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { + $t = ($k <= $bias) ? $this->_tmin : + (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); + if ($q < $t) break; + $encoded .= $this->_encode_digit(ceil($t + (($q - $t) % ($this->_base - $t)))); + $q = ($q - $t) / ($this->_base - $t); + } + $encoded .= $this->_encode_digit($q); + $bias = $this->_adapt($delta, $codecount+1, $is_first); + $codecount++; + $delta = 0; + $is_first = false; + } + } + $delta++; + $cur_code++; + } + return $encoded; + } + + /** + * Adapt the bias according to the current code point and position + * @access private + */ + function _adapt($delta, $npoints, $is_first) + { + $delta = (int) ($is_first ? ($delta / $this->_damp) : ($delta / 2)); + $delta += (int) ($delta / $npoints); + for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { + $delta = (int) ($delta / ($this->_base - $this->_tmin)); + } + return (int) ($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); + } + + /** + * Encoding a certain digit + * @access private + */ + function _encode_digit($d) + { + return chr($d + 22 + 75 * ($d < 26)); + } + + /** + * Decode a certain digit + * @access private + */ + function _decode_digit($cp) + { + $cp = ord($cp); + return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); + } + + /** + * Internal error handling method + * @access private + */ + function _error($error = '') + { + $this->_error = $error; + } + + /** + * Do Nameprep according to RFC3491 and RFC3454 + * @param array Unicode Characters + * @return string Unicode Characters, Nameprep'd + * @access private + */ + function _nameprep($input) + { + $output = array(); + $error = false; + // + // Mapping + // Walking through the input array, performing the required steps on each of + // the input chars and putting the result into the output array + // While mapping required chars we apply the cannonical ordering + + // $this->_show_hex($input); + foreach ($input as $v) { + // Map to nothing == skip that code point + if (in_array($v, $this->_np_map_nothing)) continue; + + // Try to find prohibited input + if (in_array($v, $this->_np_prohibit) || in_array($v, $this->_general_prohibited)) { + $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); + return false; + } + foreach ($this->_np_prohibit_ranges as $range) { + if ($range[0] <= $v && $v <= $range[1]) { + $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); + return false; + } + } + // + // Hangul syllable decomposition + if (0xAC00 <= $v && $v <= 0xD7AF) { + foreach ($this->_hangul_decompose($v) as $out) { + $output[] = $out; + } + // There's a decomposition mapping for that code point + } elseif (isset($this->_np_replacemaps[$v])) { + foreach ($this->_apply_cannonical_ordering($this->_np_replacemaps[$v]) as $out) { + $output[] = $out; + } + } else { + $output[] = $v; + } + } + // + // Combine code points + // + $last_class = 0; + $last_starter = 0; + $out_len = count($output); + for ($i = 0; $i < $out_len; ++$i) { + $class = $this->_get_combining_class($output[$i]); + if ((!$last_class || $last_class != $class) && $class) { + // Try to match + $seq_len = $i - $last_starter; + $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); + // On match: Replace the last starter with the composed character and remove + // the now redundant non-starter(s) + if ($out) { + $output[$last_starter] = $out; + if (count($out) != $seq_len) { + for ($j = $i+1; $j < $out_len; ++$j) { + $output[$j-1] = $output[$j]; + } + unset($output[$out_len]); + } + // Rewind the for loop by one, since there can be more possible compositions + $i--; + $out_len--; + $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]); + continue; + } + } + if (!$class) { // The current class is 0 + $last_starter = $i; + } + $last_class = $class; + } + return $output; + } + + /** + * Decomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param integer 32bit UCS4 code point + * @return array Either Hangul Syllable decomposed or original 32bit value as one value array + * @access private + */ + function _hangul_decompose($char) + { + $sindex = $char - $this->_sbase; + if ($sindex < 0 || $sindex >= $this->_scount) { + return array($char); + } + $result = array(); + $T = $this->_tbase + $sindex % $this->_tcount; + $result[] = (int) ($this->_lbase + $sindex / $this->_ncount); + $result[] = (int) ($this->_vbase + ($sindex % $this->_ncount) / $this->_tcount); + if ($T != $this->_tbase) $result[] = $T; + return $result; + } + + /** + * Ccomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul + * @param array Decomposed UCS4 sequence + * @return array UCS4 sequence with syllables composed + * @access private + */ + function _hangul_compose($input) + { + $inp_len = count($input); + if (!$inp_len) return array(); + $result = array(); + $last = $input[0]; + $result[] = $last; // copy first char from input to output + + for ($i = 1; $i < $inp_len; ++$i) { + $char = $input[$i]; + + // Find out, wether two current characters from L and V + $lindex = $last - $this->_lbase; + if (0 <= $lindex && $lindex < $this->_lcount) { + $vindex = $char - $this->_vbase; + if (0 <= $vindex && $vindex < $this->_vcount) { + // create syllable of form LV + $last = ($this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount); + $out_off = count($result) - 1; + $result[$out_off] = $last; // reset last + continue; // discard char + } + } + + // Find out, wether two current characters are LV and T + $sindex = $last - $this->_sbase; + if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount) == 0) { + $tindex = $char - $this->_tbase; + if (0 <= $tindex && $tindex <= $this->_tcount) { + // create syllable of form LVT + $last += $tindex; + $out_off = count($result) - 1; + $result[$out_off] = $last; // reset last + continue; // discard char + } + } + // if neither case was true, just add the character + $last = $char; + $result[] = $char; + } + return $result; + } + + /** + * Returns the combining class of a certain wide char + * @param integer Wide char to check (32bit integer) + * @return integer Combining class if found, else 0 + * @access private + */ + function _get_combining_class($char) + { + return isset($this->np_norm_combcls[$char]) ? $this->np_norm_combcls[$char] : 0; + } + + /** + * Apllies the cannonical ordering of a decomposed UCS4 sequence + * @param array Decomposed UCS4 sequence + * @return array Ordered USC4 sequence + * @access private + */ + function _apply_cannonical_ordering($input) + { + $swap = true; + $size = count($input); + while ($swap) { + $swap = false; + $last = $this->_get_combining_class($input[0]); + for ($i = 0; $i < $size - 1; ++$i) { + $next = $this->_get_combining_class($input[$i+1]); + if ($next != 0 && $last > $next) { + // Move item leftward until it fits + for ($j = $i + 1; $j > 0; --$j) { + if ($this->_get_combining_class($input[$j - 1]) <= $next) break; + $t = $input[$j]; + $input[$j] = $input[$j - 1]; + $input[$j - 1] = $t; + $swap = 1; + } + // Reentering the loop looking at the old character again + $next = $last; + } + $last = $next; + } + } + return $input; + } + + /** + * Do composition of a sequence of starter and non-starter + * @param array UCS4 Decomposed sequence + * @return array Ordered USC4 sequence + * @access private + */ + function _combine($input) + { + $inp_len = count($input); + // Is it a Hangul syllable? + if (1 != $inp_len) { + $hangul = $this->_hangul_compose($input); + if (count($hangul) != $inp_len) return $hangul; // This place is probably wrong + } + foreach ($this->np_casemap as $np_src => $np_target) { + if ($np_target[0] != $input[0]) continue; + if (count($np_target) != $inp_len) continue; + $hit = false; + foreach ($input as $k2 => $v2) { + if ($v2 == $np_target[$k2]) { + $hit = true; + } else { + $hit = false; + break; + } + } + if ($hit) return $np_src; + } + return false; + } + + /** + * This converts an UTF-8 encoded string to its UCS-4 representation + * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing + * each of the "chars". This is due to PHP not being able to handle strings with + * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. + * The following UTF-8 encodings are supported: + * bytes bits representation + * 1 7 0xxxxxxx + * 2 11 110xxxxx 10xxxxxx + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * Each x represents a bit that can be used to store character data. + * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 + * @access private + */ + function _utf8_to_ucs4($input) + { + $output = array(); + $out_len = 0; + $inp_len = strlen($input); + $mode = 'next'; + $test = 'none'; + for ($k = 0; $k < $inp_len; ++$k) { + $v = ord($input{$k}); // Extract byte from input string + + if ($v < 128) { // We found an ASCII char - put into stirng as is + $output[$out_len] = $v; + ++$out_len; + if ('add' == $mode) { + $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); + return false; + } + continue; + } + if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char + $start_byte = $v; + $mode = 'add'; + $test = 'range'; + if ($v >> 5 == 6) { // &110xxxxx 10xxxxx + $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left + $v = ($v - 192) << 6; + } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx + $next_byte = 1; + $v = ($v - 224) << 12; + } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 2; + $v = ($v - 240) << 18; + } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 3; + $v = ($v - 248) << 24; + } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 4; + $v = ($v - 252) << 30; + } else { + $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k); + return false; + } + if ('add' == $mode) { + $output[$out_len] = (int) $v; + ++$out_len; + continue; + } + } + if ('add' == $mode) { + if (!$this->_allow_overlong && $test == 'range') { + $test = 'none'; + if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { + $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k); + return false; + } + } + if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx + $v = ($v - 128) << ($next_byte * 6); + $output[($out_len - 1)] += $v; + --$next_byte; + } else { + $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); + return false; + } + if ($next_byte < 0) { + $mode = 'next'; + } + } + } // for + return $output; + } + + /** + * Convert UCS-4 string into UTF-8 string + * See _utf8_to_ucs4() for details + * @access private + */ + function _ucs4_to_utf8($input) + { + $output = ''; + foreach ($input as $v) { + // $v = ord($v); + if ($v < 128) { // 7bit are transferred literally + $output .= chr($v); + } elseif ($v < (1 << 11)) { // 2 bytes + $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 16)) { // 3 bytes + $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 21)) { // 4 bytes + $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } elseif ($v < (1 << 26)) { // 5 bytes + $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63)) + . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) + . chr(128 + ($v & 63)); + } elseif ($v < (1 << 31)) { // 6 bytes + $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63)) + . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); + } else { + $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k); + return false; + } + } + return $output; + } + + /** + * Convert UCS-4 array into UCS-4 string + * + * @access private + */ + function _ucs4_to_ucs4_string($input) + { + $output = ''; + // Take array values and split output to 4 bytes per value + // The bit mask is 255, which reads &11111111 + foreach ($input as $v) { + $output .= chr(($v >> 24) & 255) + . chr(($v >> 16) & 255) + . chr(($v >> 8) & 255) + . chr($v & 255); + } + return $output; + } + + /** + * Convert UCS-4 strin into UCS-4 garray + * + * @access private + */ + function _ucs4_string_to_ucs4($input) + { + $output = array(); + + $inp_len = strlen($input); + // Input length must be dividable by 4 + if ($inp_len % 4) { + $this->_error('Input UCS4 string is broken'); + return false; + } + + // Empty input - return empty output + if (!$inp_len) return $output; + + for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { + // Increment output position every 4 input bytes + if (!($i % 4)) { + $out_len++; + $output[$out_len] = 0; + } + $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); + } + return $output; + } + + /** + * Gives you a bit representation of given Byte (8 bits), Word (16 bits) or DWord (32 bits) + * Output width is automagically determined + * @access private + */ + function show_bitmask($octet) + { + if ($octet >= (1 << 16)) $w = 31; + elseif ($octet >= (1 << 8)) $w = 15; + else $w = 7; + $return = ''; + for ($i = $w; $i > -1; $i--) { + $return .= ($octet & (1 << $i)) ? 1 : '0'; + } + return $return; + } + + /** + * echo hex represnatation of UCS4 sequence to STDOUT + * @param array UCS4 sequence + * @param boolean include bitmask in output + * @return void + * @access private + */ + function _show_hex($input, $include_bit = false) + { + foreach ($input as $k => $v) { + echo '[', $k, '] => ', sprintf('%X', $v); + if ($include_bit) { + echo ' (', $this->show_bitmask($v), ')'; + } + echo "\n"; + } + } +} + +/** +* Adapter class for aligning the API of Net_IDNA_php4 with that of +* Net_IDNA +* @author Matthias Sommerfeld +*/ +class Net_IDNA extends Net_IDNA_php4 +{ + /** + * Constructor + * + * @param array $options + * @access public + * @see setParams() + */ + function Net_IDNA($options = false) + { + $this->IC = new idna_convert($options); + return $this->IC; + } + + /** + * Sets a new option value. Available options and values: + * + * [utf8 - Use either UTF-8 or ISO-8859-1 as input (true for UTF-8, false + * otherwise); The output is always UTF-8] + * [overlong - Unicode does not allow unnecessarily long encodings of chars, + * to allow this, set this parameter to true, else to false; + * default is false.] + * [strict - true: strict mode, good for registration purposes - Causes errors + * on failures; false: loose mode, ideal for "wildlife" applications + * by silently ignoring errors and returning the original input instead] + * + * @param mixed $option Parameter to set (string: single parameter; array of Parameter => Value pairs) + * @param string $value Value to use (if parameter 1 is a string) + * @return boolean true on success, false otherwise + * @access public + */ + function setParams($option, $param = false) + { + return $this->IC->set_parameters($option, $param); + } +} + +?> diff --git a/thirdparty/pear/Net/IDNA/php5.php b/thirdparty/pear/Net/IDNA/php5.php new file mode 100644 index 0000000..cc8484b --- /dev/null +++ b/thirdparty/pear/Net/IDNA/php5.php @@ -0,0 +1,3233 @@ + + * @author Matthias Sommerfeld + * @author Stefan Neufeind + * @package Net + * @version $Id: php5.php,v 1.3 2008/03/22 15:24:17 neufeind Exp $ + */ + +class Net_IDNA_php5 +{ + // {{{ npdata + /** + * These Unicode codepoints are + * mapped to nothing, See RFC3454 for details + * + * @static + * @var array + * @access private + */ + private static $_np_map_nothing = array( + 0xAD, + 0x34F, + 0x1806, + 0x180B, + 0x180C, + 0x180D, + 0x200B, + 0x200C, + 0x200D, + 0x2060, + 0xFE00, + 0xFE01, + 0xFE02, + 0xFE03, + 0xFE04, + 0xFE05, + 0xFE06, + 0xFE07, + 0xFE08, + 0xFE09, + 0xFE0A, + 0xFE0B, + 0xFE0C, + 0xFE0D, + 0xFE0E, + 0xFE0F, + 0xFEFF + ); + + /** + * Prohibited codepints + * + * @static + * @var array + * @access private + */ + private static $_general_prohibited = array( + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0xA, + 0xB, + 0xC, + 0xD, + 0xE, + 0xF, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x1C, + 0x1D, + 0x1E, + 0x1F, + 0x20, + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x2A, + 0x2B, + 0x2C, + 0x2F, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + 0x40, + 0x5B, + 0x5C, + 0x5D, + 0x5E, + 0x5F, + 0x60, + 0x7B, + 0x7C, + 0x7D, + 0x7E, + 0x7F, + 0x3002 + ); + + /** + * Codepints prohibited by Nameprep + * @static + * @var array + * @access private + */ + private static $_np_prohibit = array( + 0xA0, + 0x1680, + 0x2000, + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200A, + 0x200B, + 0x202F, + 0x205F, + 0x3000, + 0x6DD, + 0x70F, + 0x180E, + 0x200C, + 0x200D, + 0x2028, + 0x2029, + 0xFEFF, + 0xFFF9, + 0xFFFA, + 0xFFFB, + 0xFFFC, + 0xFFFE, + 0xFFFF, + 0x1FFFE, + 0x1FFFF, + 0x2FFFE, + 0x2FFFF, + 0x3FFFE, + 0x3FFFF, + 0x4FFFE, + 0x4FFFF, + 0x5FFFE, + 0x5FFFF, + 0x6FFFE, + 0x6FFFF, + 0x7FFFE, + 0x7FFFF, + 0x8FFFE, + 0x8FFFF, + 0x9FFFE, + 0x9FFFF, + 0xAFFFE, + 0xAFFFF, + 0xBFFFE, + 0xBFFFF, + 0xCFFFE, + 0xCFFFF, + 0xDFFFE, + 0xDFFFF, + 0xEFFFE, + 0xEFFFF, + 0xFFFFE, + 0xFFFFF, + 0x10FFFE, + 0x10FFFF, + 0xFFF9, + 0xFFFA, + 0xFFFB, + 0xFFFC, + 0xFFFD, + 0x340, + 0x341, + 0x200E, + 0x200F, + 0x202A, + 0x202B, + 0x202C, + 0x202D, + 0x202E, + 0x206A, + 0x206B, + 0x206C, + 0x206D, + 0x206E, + 0x206F, + 0xE0001 + ); + + /** + * Codepoint ranges prohibited by nameprep + * + * @static + * @var array + * @access private + */ + private static $_np_prohibit_ranges = array( + array(0x80, 0x9F ), + array(0x2060, 0x206F ), + array(0x1D173, 0x1D17A ), + array(0xE000, 0xF8FF ), + array(0xF0000, 0xFFFFD ), + array(0x100000, 0x10FFFD), + array(0xFDD0, 0xFDEF ), + array(0xD800, 0xDFFF ), + array(0x2FF0, 0x2FFB ), + array(0xE0020, 0xE007F ) + ); + + /** + * Replacement mappings (casemapping, replacement sequences, ...) + * + * @static + * @var array + * @access private + */ + private static $_np_replacemaps = array( + 0x41 => array(0x61), + 0x42 => array(0x62), + 0x43 => array(0x63), + 0x44 => array(0x64), + 0x45 => array(0x65), + 0x46 => array(0x66), + 0x47 => array(0x67), + 0x48 => array(0x68), + 0x49 => array(0x69), + 0x4A => array(0x6A), + 0x4B => array(0x6B), + 0x4C => array(0x6C), + 0x4D => array(0x6D), + 0x4E => array(0x6E), + 0x4F => array(0x6F), + 0x50 => array(0x70), + 0x51 => array(0x71), + 0x52 => array(0x72), + 0x53 => array(0x73), + 0x54 => array(0x74), + 0x55 => array(0x75), + 0x56 => array(0x76), + 0x57 => array(0x77), + 0x58 => array(0x78), + 0x59 => array(0x79), + 0x5A => array(0x7A), + 0xB5 => array(0x3BC), + 0xC0 => array(0xE0), + 0xC1 => array(0xE1), + 0xC2 => array(0xE2), + 0xC3 => array(0xE3), + 0xC4 => array(0xE4), + 0xC5 => array(0xE5), + 0xC6 => array(0xE6), + 0xC7 => array(0xE7), + 0xC8 => array(0xE8), + 0xC9 => array(0xE9), + 0xCA => array(0xEA), + 0xCB => array(0xEB), + 0xCC => array(0xEC), + 0xCD => array(0xED), + 0xCE => array(0xEE), + 0xCF => array(0xEF), + 0xD0 => array(0xF0), + 0xD1 => array(0xF1), + 0xD2 => array(0xF2), + 0xD3 => array(0xF3), + 0xD4 => array(0xF4), + 0xD5 => array(0xF5), + 0xD6 => array(0xF6), + 0xD8 => array(0xF8), + 0xD9 => array(0xF9), + 0xDA => array(0xFA), + 0xDB => array(0xFB), + 0xDC => array(0xFC), + 0xDD => array(0xFD), + 0xDE => array(0xFE), + 0xDF => array(0x73, 0x73), + 0x100 => array(0x101), + 0x102 => array(0x103), + 0x104 => array(0x105), + 0x106 => array(0x107), + 0x108 => array(0x109), + 0x10A => array(0x10B), + 0x10C => array(0x10D), + 0x10E => array(0x10F), + 0x110 => array(0x111), + 0x112 => array(0x113), + 0x114 => array(0x115), + 0x116 => array(0x117), + 0x118 => array(0x119), + 0x11A => array(0x11B), + 0x11C => array(0x11D), + 0x11E => array(0x11F), + 0x120 => array(0x121), + 0x122 => array(0x123), + 0x124 => array(0x125), + 0x126 => array(0x127), + 0x128 => array(0x129), + 0x12A => array(0x12B), + 0x12C => array(0x12D), + 0x12E => array(0x12F), + 0x130 => array(0x69, 0x307), + 0x132 => array(0x133), + 0x134 => array(0x135), + 0x136 => array(0x137), + 0x139 => array(0x13A), + 0x13B => array(0x13C), + 0x13D => array(0x13E), + 0x13F => array(0x140), + 0x141 => array(0x142), + 0x143 => array(0x144), + 0x145 => array(0x146), + 0x147 => array(0x148), + 0x149 => array(0x2BC, 0x6E), + 0x14A => array(0x14B), + 0x14C => array(0x14D), + 0x14E => array(0x14F), + 0x150 => array(0x151), + 0x152 => array(0x153), + 0x154 => array(0x155), + 0x156 => array(0x157), + 0x158 => array(0x159), + 0x15A => array(0x15B), + 0x15C => array(0x15D), + 0x15E => array(0x15F), + 0x160 => array(0x161), + 0x162 => array(0x163), + 0x164 => array(0x165), + 0x166 => array(0x167), + 0x168 => array(0x169), + 0x16A => array(0x16B), + 0x16C => array(0x16D), + 0x16E => array(0x16F), + 0x170 => array(0x171), + 0x172 => array(0x173), + 0x174 => array(0x175), + 0x176 => array(0x177), + 0x178 => array(0xFF), + 0x179 => array(0x17A), + 0x17B => array(0x17C), + 0x17D => array(0x17E), + 0x17F => array(0x73), + 0x181 => array(0x253), + 0x182 => array(0x183), + 0x184 => array(0x185), + 0x186 => array(0x254), + 0x187 => array(0x188), + 0x189 => array(0x256), + 0x18A => array(0x257), + 0x18B => array(0x18C), + 0x18E => array(0x1DD), + 0x18F => array(0x259), + 0x190 => array(0x25B), + 0x191 => array(0x192), + 0x193 => array(0x260), + 0x194 => array(0x263), + 0x196 => array(0x269), + 0x197 => array(0x268), + 0x198 => array(0x199), + 0x19C => array(0x26F), + 0x19D => array(0x272), + 0x19F => array(0x275), + 0x1A0 => array(0x1A1), + 0x1A2 => array(0x1A3), + 0x1A4 => array(0x1A5), + 0x1A6 => array(0x280), + 0x1A7 => array(0x1A8), + 0x1A9 => array(0x283), + 0x1AC => array(0x1AD), + 0x1AE => array(0x288), + 0x1AF => array(0x1B0), + 0x1B1 => array(0x28A), + 0x1B2 => array(0x28B), + 0x1B3 => array(0x1B4), + 0x1B5 => array(0x1B6), + 0x1B7 => array(0x292), + 0x1B8 => array(0x1B9), + 0x1BC => array(0x1BD), + 0x1C4 => array(0x1C6), + 0x1C5 => array(0x1C6), + 0x1C7 => array(0x1C9), + 0x1C8 => array(0x1C9), + 0x1CA => array(0x1CC), + 0x1CB => array(0x1CC), + 0x1CD => array(0x1CE), + 0x1CF => array(0x1D0), + 0x1D1 => array(0x1D2), + 0x1D3 => array(0x1D4), + 0x1D5 => array(0x1D6), + 0x1D7 => array(0x1D8), + 0x1D9 => array(0x1DA), + 0x1DB => array(0x1DC), + 0x1DE => array(0x1DF), + 0x1E0 => array(0x1E1), + 0x1E2 => array(0x1E3), + 0x1E4 => array(0x1E5), + 0x1E6 => array(0x1E7), + 0x1E8 => array(0x1E9), + 0x1EA => array(0x1EB), + 0x1EC => array(0x1ED), + 0x1EE => array(0x1EF), + 0x1F0 => array(0x6A, 0x30C), + 0x1F1 => array(0x1F3), + 0x1F2 => array(0x1F3), + 0x1F4 => array(0x1F5), + 0x1F6 => array(0x195), + 0x1F7 => array(0x1BF), + 0x1F8 => array(0x1F9), + 0x1FA => array(0x1FB), + 0x1FC => array(0x1FD), + 0x1FE => array(0x1FF), + 0x200 => array(0x201), + 0x202 => array(0x203), + 0x204 => array(0x205), + 0x206 => array(0x207), + 0x208 => array(0x209), + 0x20A => array(0x20B), + 0x20C => array(0x20D), + 0x20E => array(0x20F), + 0x210 => array(0x211), + 0x212 => array(0x213), + 0x214 => array(0x215), + 0x216 => array(0x217), + 0x218 => array(0x219), + 0x21A => array(0x21B), + 0x21C => array(0x21D), + 0x21E => array(0x21F), + 0x220 => array(0x19E), + 0x222 => array(0x223), + 0x224 => array(0x225), + 0x226 => array(0x227), + 0x228 => array(0x229), + 0x22A => array(0x22B), + 0x22C => array(0x22D), + 0x22E => array(0x22F), + 0x230 => array(0x231), + 0x232 => array(0x233), + 0x345 => array(0x3B9), + 0x37A => array(0x20, 0x3B9), + 0x386 => array(0x3AC), + 0x388 => array(0x3AD), + 0x389 => array(0x3AE), + 0x38A => array(0x3AF), + 0x38C => array(0x3CC), + 0x38E => array(0x3CD), + 0x38F => array(0x3CE), + 0x390 => array(0x3B9, 0x308, 0x301), + 0x391 => array(0x3B1), + 0x392 => array(0x3B2), + 0x393 => array(0x3B3), + 0x394 => array(0x3B4), + 0x395 => array(0x3B5), + 0x396 => array(0x3B6), + 0x397 => array(0x3B7), + 0x398 => array(0x3B8), + 0x399 => array(0x3B9), + 0x39A => array(0x3BA), + 0x39B => array(0x3BB), + 0x39C => array(0x3BC), + 0x39D => array(0x3BD), + 0x39E => array(0x3BE), + 0x39F => array(0x3BF), + 0x3A0 => array(0x3C0), + 0x3A1 => array(0x3C1), + 0x3A3 => array(0x3C3), + 0x3A4 => array(0x3C4), + 0x3A5 => array(0x3C5), + 0x3A6 => array(0x3C6), + 0x3A7 => array(0x3C7), + 0x3A8 => array(0x3C8), + 0x3A9 => array(0x3C9), + 0x3AA => array(0x3CA), + 0x3AB => array(0x3CB), + 0x3B0 => array(0x3C5, 0x308, 0x301), + 0x3C2 => array(0x3C3), + 0x3D0 => array(0x3B2), + 0x3D1 => array(0x3B8), + 0x3D2 => array(0x3C5), + 0x3D3 => array(0x3CD), + 0x3D4 => array(0x3CB), + 0x3D5 => array(0x3C6), + 0x3D6 => array(0x3C0), + 0x3D8 => array(0x3D9), + 0x3DA => array(0x3DB), + 0x3DC => array(0x3DD), + 0x3DE => array(0x3DF), + 0x3E0 => array(0x3E1), + 0x3E2 => array(0x3E3), + 0x3E4 => array(0x3E5), + 0x3E6 => array(0x3E7), + 0x3E8 => array(0x3E9), + 0x3EA => array(0x3EB), + 0x3EC => array(0x3ED), + 0x3EE => array(0x3EF), + 0x3F0 => array(0x3BA), + 0x3F1 => array(0x3C1), + 0x3F2 => array(0x3C3), + 0x3F4 => array(0x3B8), + 0x3F5 => array(0x3B5), + 0x400 => array(0x450), + 0x401 => array(0x451), + 0x402 => array(0x452), + 0x403 => array(0x453), + 0x404 => array(0x454), + 0x405 => array(0x455), + 0x406 => array(0x456), + 0x407 => array(0x457), + 0x408 => array(0x458), + 0x409 => array(0x459), + 0x40A => array(0x45A), + 0x40B => array(0x45B), + 0x40C => array(0x45C), + 0x40D => array(0x45D), + 0x40E => array(0x45E), + 0x40F => array(0x45F), + 0x410 => array(0x430), + 0x411 => array(0x431), + 0x412 => array(0x432), + 0x413 => array(0x433), + 0x414 => array(0x434), + 0x415 => array(0x435), + 0x416 => array(0x436), + 0x417 => array(0x437), + 0x418 => array(0x438), + 0x419 => array(0x439), + 0x41A => array(0x43A), + 0x41B => array(0x43B), + 0x41C => array(0x43C), + 0x41D => array(0x43D), + 0x41E => array(0x43E), + 0x41F => array(0x43F), + 0x420 => array(0x440), + 0x421 => array(0x441), + 0x422 => array(0x442), + 0x423 => array(0x443), + 0x424 => array(0x444), + 0x425 => array(0x445), + 0x426 => array(0x446), + 0x427 => array(0x447), + 0x428 => array(0x448), + 0x429 => array(0x449), + 0x42A => array(0x44A), + 0x42B => array(0x44B), + 0x42C => array(0x44C), + 0x42D => array(0x44D), + 0x42E => array(0x44E), + 0x42F => array(0x44F), + 0x460 => array(0x461), + 0x462 => array(0x463), + 0x464 => array(0x465), + 0x466 => array(0x467), + 0x468 => array(0x469), + 0x46A => array(0x46B), + 0x46C => array(0x46D), + 0x46E => array(0x46F), + 0x470 => array(0x471), + 0x472 => array(0x473), + 0x474 => array(0x475), + 0x476 => array(0x477), + 0x478 => array(0x479), + 0x47A => array(0x47B), + 0x47C => array(0x47D), + 0x47E => array(0x47F), + 0x480 => array(0x481), + 0x48A => array(0x48B), + 0x48C => array(0x48D), + 0x48E => array(0x48F), + 0x490 => array(0x491), + 0x492 => array(0x493), + 0x494 => array(0x495), + 0x496 => array(0x497), + 0x498 => array(0x499), + 0x49A => array(0x49B), + 0x49C => array(0x49D), + 0x49E => array(0x49F), + 0x4A0 => array(0x4A1), + 0x4A2 => array(0x4A3), + 0x4A4 => array(0x4A5), + 0x4A6 => array(0x4A7), + 0x4A8 => array(0x4A9), + 0x4AA => array(0x4AB), + 0x4AC => array(0x4AD), + 0x4AE => array(0x4AF), + 0x4B0 => array(0x4B1), + 0x4B2 => array(0x4B3), + 0x4B4 => array(0x4B5), + 0x4B6 => array(0x4B7), + 0x4B8 => array(0x4B9), + 0x4BA => array(0x4BB), + 0x4BC => array(0x4BD), + 0x4BE => array(0x4BF), + 0x4C1 => array(0x4C2), + 0x4C3 => array(0x4C4), + 0x4C5 => array(0x4C6), + 0x4C7 => array(0x4C8), + 0x4C9 => array(0x4CA), + 0x4CB => array(0x4CC), + 0x4CD => array(0x4CE), + 0x4D0 => array(0x4D1), + 0x4D2 => array(0x4D3), + 0x4D4 => array(0x4D5), + 0x4D6 => array(0x4D7), + 0x4D8 => array(0x4D9), + 0x4DA => array(0x4DB), + 0x4DC => array(0x4DD), + 0x4DE => array(0x4DF), + 0x4E0 => array(0x4E1), + 0x4E2 => array(0x4E3), + 0x4E4 => array(0x4E5), + 0x4E6 => array(0x4E7), + 0x4E8 => array(0x4E9), + 0x4EA => array(0x4EB), + 0x4EC => array(0x4ED), + 0x4EE => array(0x4EF), + 0x4F0 => array(0x4F1), + 0x4F2 => array(0x4F3), + 0x4F4 => array(0x4F5), + 0x4F8 => array(0x4F9), + 0x500 => array(0x501), + 0x502 => array(0x503), + 0x504 => array(0x505), + 0x506 => array(0x507), + 0x508 => array(0x509), + 0x50A => array(0x50B), + 0x50C => array(0x50D), + 0x50E => array(0x50F), + 0x531 => array(0x561), + 0x532 => array(0x562), + 0x533 => array(0x563), + 0x534 => array(0x564), + 0x535 => array(0x565), + 0x536 => array(0x566), + 0x537 => array(0x567), + 0x538 => array(0x568), + 0x539 => array(0x569), + 0x53A => array(0x56A), + 0x53B => array(0x56B), + 0x53C => array(0x56C), + 0x53D => array(0x56D), + 0x53E => array(0x56E), + 0x53F => array(0x56F), + 0x540 => array(0x570), + 0x541 => array(0x571), + 0x542 => array(0x572), + 0x543 => array(0x573), + 0x544 => array(0x574), + 0x545 => array(0x575), + 0x546 => array(0x576), + 0x547 => array(0x577), + 0x548 => array(0x578), + 0x549 => array(0x579), + 0x54A => array(0x57A), + 0x54B => array(0x57B), + 0x54C => array(0x57C), + 0x54D => array(0x57D), + 0x54E => array(0x57E), + 0x54F => array(0x57F), + 0x550 => array(0x580), + 0x551 => array(0x581), + 0x552 => array(0x582), + 0x553 => array(0x583), + 0x554 => array(0x584), + 0x555 => array(0x585), + 0x556 => array(0x586), + 0x587 => array(0x565, 0x582), + 0x1E00 => array(0x1E01), + 0x1E02 => array(0x1E03), + 0x1E04 => array(0x1E05), + 0x1E06 => array(0x1E07), + 0x1E08 => array(0x1E09), + 0x1E0A => array(0x1E0B), + 0x1E0C => array(0x1E0D), + 0x1E0E => array(0x1E0F), + 0x1E10 => array(0x1E11), + 0x1E12 => array(0x1E13), + 0x1E14 => array(0x1E15), + 0x1E16 => array(0x1E17), + 0x1E18 => array(0x1E19), + 0x1E1A => array(0x1E1B), + 0x1E1C => array(0x1E1D), + 0x1E1E => array(0x1E1F), + 0x1E20 => array(0x1E21), + 0x1E22 => array(0x1E23), + 0x1E24 => array(0x1E25), + 0x1E26 => array(0x1E27), + 0x1E28 => array(0x1E29), + 0x1E2A => array(0x1E2B), + 0x1E2C => array(0x1E2D), + 0x1E2E => array(0x1E2F), + 0x1E30 => array(0x1E31), + 0x1E32 => array(0x1E33), + 0x1E34 => array(0x1E35), + 0x1E36 => array(0x1E37), + 0x1E38 => array(0x1E39), + 0x1E3A => array(0x1E3B), + 0x1E3C => array(0x1E3D), + 0x1E3E => array(0x1E3F), + 0x1E40 => array(0x1E41), + 0x1E42 => array(0x1E43), + 0x1E44 => array(0x1E45), + 0x1E46 => array(0x1E47), + 0x1E48 => array(0x1E49), + 0x1E4A => array(0x1E4B), + 0x1E4C => array(0x1E4D), + 0x1E4E => array(0x1E4F), + 0x1E50 => array(0x1E51), + 0x1E52 => array(0x1E53), + 0x1E54 => array(0x1E55), + 0x1E56 => array(0x1E57), + 0x1E58 => array(0x1E59), + 0x1E5A => array(0x1E5B), + 0x1E5C => array(0x1E5D), + 0x1E5E => array(0x1E5F), + 0x1E60 => array(0x1E61), + 0x1E62 => array(0x1E63), + 0x1E64 => array(0x1E65), + 0x1E66 => array(0x1E67), + 0x1E68 => array(0x1E69), + 0x1E6A => array(0x1E6B), + 0x1E6C => array(0x1E6D), + 0x1E6E => array(0x1E6F), + 0x1E70 => array(0x1E71), + 0x1E72 => array(0x1E73), + 0x1E74 => array(0x1E75), + 0x1E76 => array(0x1E77), + 0x1E78 => array(0x1E79), + 0x1E7A => array(0x1E7B), + 0x1E7C => array(0x1E7D), + 0x1E7E => array(0x1E7F), + 0x1E80 => array(0x1E81), + 0x1E82 => array(0x1E83), + 0x1E84 => array(0x1E85), + 0x1E86 => array(0x1E87), + 0x1E88 => array(0x1E89), + 0x1E8A => array(0x1E8B), + 0x1E8C => array(0x1E8D), + 0x1E8E => array(0x1E8F), + 0x1E90 => array(0x1E91), + 0x1E92 => array(0x1E93), + 0x1E94 => array(0x1E95), + 0x1E96 => array(0x68, 0x331), + 0x1E97 => array(0x74, 0x308), + 0x1E98 => array(0x77, 0x30A), + 0x1E99 => array(0x79, 0x30A), + 0x1E9A => array(0x61, 0x2BE), + 0x1E9B => array(0x1E61), + 0x1EA0 => array(0x1EA1), + 0x1EA2 => array(0x1EA3), + 0x1EA4 => array(0x1EA5), + 0x1EA6 => array(0x1EA7), + 0x1EA8 => array(0x1EA9), + 0x1EAA => array(0x1EAB), + 0x1EAC => array(0x1EAD), + 0x1EAE => array(0x1EAF), + 0x1EB0 => array(0x1EB1), + 0x1EB2 => array(0x1EB3), + 0x1EB4 => array(0x1EB5), + 0x1EB6 => array(0x1EB7), + 0x1EB8 => array(0x1EB9), + 0x1EBA => array(0x1EBB), + 0x1EBC => array(0x1EBD), + 0x1EBE => array(0x1EBF), + 0x1EC0 => array(0x1EC1), + 0x1EC2 => array(0x1EC3), + 0x1EC4 => array(0x1EC5), + 0x1EC6 => array(0x1EC7), + 0x1EC8 => array(0x1EC9), + 0x1ECA => array(0x1ECB), + 0x1ECC => array(0x1ECD), + 0x1ECE => array(0x1ECF), + 0x1ED0 => array(0x1ED1), + 0x1ED2 => array(0x1ED3), + 0x1ED4 => array(0x1ED5), + 0x1ED6 => array(0x1ED7), + 0x1ED8 => array(0x1ED9), + 0x1EDA => array(0x1EDB), + 0x1EDC => array(0x1EDD), + 0x1EDE => array(0x1EDF), + 0x1EE0 => array(0x1EE1), + 0x1EE2 => array(0x1EE3), + 0x1EE4 => array(0x1EE5), + 0x1EE6 => array(0x1EE7), + 0x1EE8 => array(0x1EE9), + 0x1EEA => array(0x1EEB), + 0x1EEC => array(0x1EED), + 0x1EEE => array(0x1EEF), + 0x1EF0 => array(0x1EF1), + 0x1EF2 => array(0x1EF3), + 0x1EF4 => array(0x1EF5), + 0x1EF6 => array(0x1EF7), + 0x1EF8 => array(0x1EF9), + 0x1F08 => array(0x1F00), + 0x1F09 => array(0x1F01), + 0x1F0A => array(0x1F02), + 0x1F0B => array(0x1F03), + 0x1F0C => array(0x1F04), + 0x1F0D => array(0x1F05), + 0x1F0E => array(0x1F06), + 0x1F0F => array(0x1F07), + 0x1F18 => array(0x1F10), + 0x1F19 => array(0x1F11), + 0x1F1A => array(0x1F12), + 0x1F1B => array(0x1F13), + 0x1F1C => array(0x1F14), + 0x1F1D => array(0x1F15), + 0x1F28 => array(0x1F20), + 0x1F29 => array(0x1F21), + 0x1F2A => array(0x1F22), + 0x1F2B => array(0x1F23), + 0x1F2C => array(0x1F24), + 0x1F2D => array(0x1F25), + 0x1F2E => array(0x1F26), + 0x1F2F => array(0x1F27), + 0x1F38 => array(0x1F30), + 0x1F39 => array(0x1F31), + 0x1F3A => array(0x1F32), + 0x1F3B => array(0x1F33), + 0x1F3C => array(0x1F34), + 0x1F3D => array(0x1F35), + 0x1F3E => array(0x1F36), + 0x1F3F => array(0x1F37), + 0x1F48 => array(0x1F40), + 0x1F49 => array(0x1F41), + 0x1F4A => array(0x1F42), + 0x1F4B => array(0x1F43), + 0x1F4C => array(0x1F44), + 0x1F4D => array(0x1F45), + 0x1F50 => array(0x3C5, 0x313), + 0x1F52 => array(0x3C5, 0x313, 0x300), + 0x1F54 => array(0x3C5, 0x313, 0x301), + 0x1F56 => array(0x3C5, 0x313, 0x342), + 0x1F59 => array(0x1F51), + 0x1F5B => array(0x1F53), + 0x1F5D => array(0x1F55), + 0x1F5F => array(0x1F57), + 0x1F68 => array(0x1F60), + 0x1F69 => array(0x1F61), + 0x1F6A => array(0x1F62), + 0x1F6B => array(0x1F63), + 0x1F6C => array(0x1F64), + 0x1F6D => array(0x1F65), + 0x1F6E => array(0x1F66), + 0x1F6F => array(0x1F67), + 0x1F80 => array(0x1F00, 0x3B9), + 0x1F81 => array(0x1F01, 0x3B9), + 0x1F82 => array(0x1F02, 0x3B9), + 0x1F83 => array(0x1F03, 0x3B9), + 0x1F84 => array(0x1F04, 0x3B9), + 0x1F85 => array(0x1F05, 0x3B9), + 0x1F86 => array(0x1F06, 0x3B9), + 0x1F87 => array(0x1F07, 0x3B9), + 0x1F88 => array(0x1F00, 0x3B9), + 0x1F89 => array(0x1F01, 0x3B9), + 0x1F8A => array(0x1F02, 0x3B9), + 0x1F8B => array(0x1F03, 0x3B9), + 0x1F8C => array(0x1F04, 0x3B9), + 0x1F8D => array(0x1F05, 0x3B9), + 0x1F8E => array(0x1F06, 0x3B9), + 0x1F8F => array(0x1F07, 0x3B9), + 0x1F90 => array(0x1F20, 0x3B9), + 0x1F91 => array(0x1F21, 0x3B9), + 0x1F92 => array(0x1F22, 0x3B9), + 0x1F93 => array(0x1F23, 0x3B9), + 0x1F94 => array(0x1F24, 0x3B9), + 0x1F95 => array(0x1F25, 0x3B9), + 0x1F96 => array(0x1F26, 0x3B9), + 0x1F97 => array(0x1F27, 0x3B9), + 0x1F98 => array(0x1F20, 0x3B9), + 0x1F99 => array(0x1F21, 0x3B9), + 0x1F9A => array(0x1F22, 0x3B9), + 0x1F9B => array(0x1F23, 0x3B9), + 0x1F9C => array(0x1F24, 0x3B9), + 0x1F9D => array(0x1F25, 0x3B9), + 0x1F9E => array(0x1F26, 0x3B9), + 0x1F9F => array(0x1F27, 0x3B9), + 0x1FA0 => array(0x1F60, 0x3B9), + 0x1FA1 => array(0x1F61, 0x3B9), + 0x1FA2 => array(0x1F62, 0x3B9), + 0x1FA3 => array(0x1F63, 0x3B9), + 0x1FA4 => array(0x1F64, 0x3B9), + 0x1FA5 => array(0x1F65, 0x3B9), + 0x1FA6 => array(0x1F66, 0x3B9), + 0x1FA7 => array(0x1F67, 0x3B9), + 0x1FA8 => array(0x1F60, 0x3B9), + 0x1FA9 => array(0x1F61, 0x3B9), + 0x1FAA => array(0x1F62, 0x3B9), + 0x1FAB => array(0x1F63, 0x3B9), + 0x1FAC => array(0x1F64, 0x3B9), + 0x1FAD => array(0x1F65, 0x3B9), + 0x1FAE => array(0x1F66, 0x3B9), + 0x1FAF => array(0x1F67, 0x3B9), + 0x1FB2 => array(0x1F70, 0x3B9), + 0x1FB3 => array(0x3B1, 0x3B9), + 0x1FB4 => array(0x3AC, 0x3B9), + 0x1FB6 => array(0x3B1, 0x342), + 0x1FB7 => array(0x3B1, 0x342, 0x3B9), + 0x1FB8 => array(0x1FB0), + 0x1FB9 => array(0x1FB1), + 0x1FBA => array(0x1F70), + 0x1FBB => array(0x1F71), + 0x1FBC => array(0x3B1, 0x3B9), + 0x1FBE => array(0x3B9), + 0x1FC2 => array(0x1F74, 0x3B9), + 0x1FC3 => array(0x3B7, 0x3B9), + 0x1FC4 => array(0x3AE, 0x3B9), + 0x1FC6 => array(0x3B7, 0x342), + 0x1FC7 => array(0x3B7, 0x342, 0x3B9), + 0x1FC8 => array(0x1F72), + 0x1FC9 => array(0x1F73), + 0x1FCA => array(0x1F74), + 0x1FCB => array(0x1F75), + 0x1FCC => array(0x3B7, 0x3B9), + 0x1FD2 => array(0x3B9, 0x308, 0x300), + 0x1FD3 => array(0x3B9, 0x308, 0x301), + 0x1FD6 => array(0x3B9, 0x342), + 0x1FD7 => array(0x3B9, 0x308, 0x342), + 0x1FD8 => array(0x1FD0), + 0x1FD9 => array(0x1FD1), + 0x1FDA => array(0x1F76), + 0x1FDB => array(0x1F77), + 0x1FE2 => array(0x3C5, 0x308, 0x300), + 0x1FE3 => array(0x3C5, 0x308, 0x301), + 0x1FE4 => array(0x3C1, 0x313), + 0x1FE6 => array(0x3C5, 0x342), + 0x1FE7 => array(0x3C5, 0x308, 0x342), + 0x1FE8 => array(0x1FE0), + 0x1FE9 => array(0x1FE1), + 0x1FEA => array(0x1F7A), + 0x1FEB => array(0x1F7B), + 0x1FEC => array(0x1FE5), + 0x1FF2 => array(0x1F7C, 0x3B9), + 0x1FF3 => array(0x3C9, 0x3B9), + 0x1FF4 => array(0x3CE, 0x3B9), + 0x1FF6 => array(0x3C9, 0x342), + 0x1FF7 => array(0x3C9, 0x342, 0x3B9), + 0x1FF8 => array(0x1F78), + 0x1FF9 => array(0x1F79), + 0x1FFA => array(0x1F7C), + 0x1FFB => array(0x1F7D), + 0x1FFC => array(0x3C9, 0x3B9), + 0x20A8 => array(0x72, 0x73), + 0x2102 => array(0x63), + 0x2103 => array(0xB0, 0x63), + 0x2107 => array(0x25B), + 0x2109 => array(0xB0, 0x66), + 0x210B => array(0x68), + 0x210C => array(0x68), + 0x210D => array(0x68), + 0x2110 => array(0x69), + 0x2111 => array(0x69), + 0x2112 => array(0x6C), + 0x2115 => array(0x6E), + 0x2116 => array(0x6E, 0x6F), + 0x2119 => array(0x70), + 0x211A => array(0x71), + 0x211B => array(0x72), + 0x211C => array(0x72), + 0x211D => array(0x72), + 0x2120 => array(0x73, 0x6D), + 0x2121 => array(0x74, 0x65, 0x6C), + 0x2122 => array(0x74, 0x6D), + 0x2124 => array(0x7A), + 0x2126 => array(0x3C9), + 0x2128 => array(0x7A), + 0x212A => array(0x6B), + 0x212B => array(0xE5), + 0x212C => array(0x62), + 0x212D => array(0x63), + 0x2130 => array(0x65), + 0x2131 => array(0x66), + 0x2133 => array(0x6D), + 0x213E => array(0x3B3), + 0x213F => array(0x3C0), + 0x2145 => array(0x64), + 0x2160 => array(0x2170), + 0x2161 => array(0x2171), + 0x2162 => array(0x2172), + 0x2163 => array(0x2173), + 0x2164 => array(0x2174), + 0x2165 => array(0x2175), + 0x2166 => array(0x2176), + 0x2167 => array(0x2177), + 0x2168 => array(0x2178), + 0x2169 => array(0x2179), + 0x216A => array(0x217A), + 0x216B => array(0x217B), + 0x216C => array(0x217C), + 0x216D => array(0x217D), + 0x216E => array(0x217E), + 0x216F => array(0x217F), + 0x24B6 => array(0x24D0), + 0x24B7 => array(0x24D1), + 0x24B8 => array(0x24D2), + 0x24B9 => array(0x24D3), + 0x24BA => array(0x24D4), + 0x24BB => array(0x24D5), + 0x24BC => array(0x24D6), + 0x24BD => array(0x24D7), + 0x24BE => array(0x24D8), + 0x24BF => array(0x24D9), + 0x24C0 => array(0x24DA), + 0x24C1 => array(0x24DB), + 0x24C2 => array(0x24DC), + 0x24C3 => array(0x24DD), + 0x24C4 => array(0x24DE), + 0x24C5 => array(0x24DF), + 0x24C6 => array(0x24E0), + 0x24C7 => array(0x24E1), + 0x24C8 => array(0x24E2), + 0x24C9 => array(0x24E3), + 0x24CA => array(0x24E4), + 0x24CB => array(0x24E5), + 0x24CC => array(0x24E6), + 0x24CD => array(0x24E7), + 0x24CE => array(0x24E8), + 0x24CF => array(0x24E9), + 0x3371 => array(0x68, 0x70, 0x61), + 0x3373 => array(0x61, 0x75), + 0x3375 => array(0x6F, 0x76), + 0x3380 => array(0x70, 0x61), + 0x3381 => array(0x6E, 0x61), + 0x3382 => array(0x3BC, 0x61), + 0x3383 => array(0x6D, 0x61), + 0x3384 => array(0x6B, 0x61), + 0x3385 => array(0x6B, 0x62), + 0x3386 => array(0x6D, 0x62), + 0x3387 => array(0x67, 0x62), + 0x338A => array(0x70, 0x66), + 0x338B => array(0x6E, 0x66), + 0x338C => array(0x3BC, 0x66), + 0x3390 => array(0x68, 0x7A), + 0x3391 => array(0x6B, 0x68, 0x7A), + 0x3392 => array(0x6D, 0x68, 0x7A), + 0x3393 => array(0x67, 0x68, 0x7A), + 0x3394 => array(0x74, 0x68, 0x7A), + 0x33A9 => array(0x70, 0x61), + 0x33AA => array(0x6B, 0x70, 0x61), + 0x33AB => array(0x6D, 0x70, 0x61), + 0x33AC => array(0x67, 0x70, 0x61), + 0x33B4 => array(0x70, 0x76), + 0x33B5 => array(0x6E, 0x76), + 0x33B6 => array(0x3BC, 0x76), + 0x33B7 => array(0x6D, 0x76), + 0x33B8 => array(0x6B, 0x76), + 0x33B9 => array(0x6D, 0x76), + 0x33BA => array(0x70, 0x77), + 0x33BB => array(0x6E, 0x77), + 0x33BC => array(0x3BC, 0x77), + 0x33BD => array(0x6D, 0x77), + 0x33BE => array(0x6B, 0x77), + 0x33BF => array(0x6D, 0x77), + 0x33C0 => array(0x6B, 0x3C9), + 0x33C1 => array(0x6D, 0x3C9), /* + 0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E), */ + 0x33C3 => array(0x62, 0x71), + 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67), + 0x33C7 => array(0x63, 0x6F, 0x2E), + 0x33C8 => array(0x64, 0x62), + 0x33C9 => array(0x67, 0x79), + 0x33CB => array(0x68, 0x70), + 0x33CD => array(0x6B, 0x6B), + 0x33CE => array(0x6B, 0x6D), + 0x33D7 => array(0x70, 0x68), + 0x33D9 => array(0x70, 0x70, 0x6D), + 0x33DA => array(0x70, 0x72), + 0x33DC => array(0x73, 0x76), + 0x33DD => array(0x77, 0x62), + 0xFB00 => array(0x66, 0x66), + 0xFB01 => array(0x66, 0x69), + 0xFB02 => array(0x66, 0x6C), + 0xFB03 => array(0x66, 0x66, 0x69), + 0xFB04 => array(0x66, 0x66, 0x6C), + 0xFB05 => array(0x73, 0x74), + 0xFB06 => array(0x73, 0x74), + 0xFB13 => array(0x574, 0x576), + 0xFB14 => array(0x574, 0x565), + 0xFB15 => array(0x574, 0x56B), + 0xFB16 => array(0x57E, 0x576), + 0xFB17 => array(0x574, 0x56D), + 0xFF21 => array(0xFF41), + 0xFF22 => array(0xFF42), + 0xFF23 => array(0xFF43), + 0xFF24 => array(0xFF44), + 0xFF25 => array(0xFF45), + 0xFF26 => array(0xFF46), + 0xFF27 => array(0xFF47), + 0xFF28 => array(0xFF48), + 0xFF29 => array(0xFF49), + 0xFF2A => array(0xFF4A), + 0xFF2B => array(0xFF4B), + 0xFF2C => array(0xFF4C), + 0xFF2D => array(0xFF4D), + 0xFF2E => array(0xFF4E), + 0xFF2F => array(0xFF4F), + 0xFF30 => array(0xFF50), + 0xFF31 => array(0xFF51), + 0xFF32 => array(0xFF52), + 0xFF33 => array(0xFF53), + 0xFF34 => array(0xFF54), + 0xFF35 => array(0xFF55), + 0xFF36 => array(0xFF56), + 0xFF37 => array(0xFF57), + 0xFF38 => array(0xFF58), + 0xFF39 => array(0xFF59), + 0xFF3A => array(0xFF5A), + 0x10400 => array(0x10428), + 0x10401 => array(0x10429), + 0x10402 => array(0x1042A), + 0x10403 => array(0x1042B), + 0x10404 => array(0x1042C), + 0x10405 => array(0x1042D), + 0x10406 => array(0x1042E), + 0x10407 => array(0x1042F), + 0x10408 => array(0x10430), + 0x10409 => array(0x10431), + 0x1040A => array(0x10432), + 0x1040B => array(0x10433), + 0x1040C => array(0x10434), + 0x1040D => array(0x10435), + 0x1040E => array(0x10436), + 0x1040F => array(0x10437), + 0x10410 => array(0x10438), + 0x10411 => array(0x10439), + 0x10412 => array(0x1043A), + 0x10413 => array(0x1043B), + 0x10414 => array(0x1043C), + 0x10415 => array(0x1043D), + 0x10416 => array(0x1043E), + 0x10417 => array(0x1043F), + 0x10418 => array(0x10440), + 0x10419 => array(0x10441), + 0x1041A => array(0x10442), + 0x1041B => array(0x10443), + 0x1041C => array(0x10444), + 0x1041D => array(0x10445), + 0x1041E => array(0x10446), + 0x1041F => array(0x10447), + 0x10420 => array(0x10448), + 0x10421 => array(0x10449), + 0x10422 => array(0x1044A), + 0x10423 => array(0x1044B), + 0x10424 => array(0x1044C), + 0x10425 => array(0x1044D), + 0x1D400 => array(0x61), + 0x1D401 => array(0x62), + 0x1D402 => array(0x63), + 0x1D403 => array(0x64), + 0x1D404 => array(0x65), + 0x1D405 => array(0x66), + 0x1D406 => array(0x67), + 0x1D407 => array(0x68), + 0x1D408 => array(0x69), + 0x1D409 => array(0x6A), + 0x1D40A => array(0x6B), + 0x1D40B => array(0x6C), + 0x1D40C => array(0x6D), + 0x1D40D => array(0x6E), + 0x1D40E => array(0x6F), + 0x1D40F => array(0x70), + 0x1D410 => array(0x71), + 0x1D411 => array(0x72), + 0x1D412 => array(0x73), + 0x1D413 => array(0x74), + 0x1D414 => array(0x75), + 0x1D415 => array(0x76), + 0x1D416 => array(0x77), + 0x1D417 => array(0x78), + 0x1D418 => array(0x79), + 0x1D419 => array(0x7A), + 0x1D434 => array(0x61), + 0x1D435 => array(0x62), + 0x1D436 => array(0x63), + 0x1D437 => array(0x64), + 0x1D438 => array(0x65), + 0x1D439 => array(0x66), + 0x1D43A => array(0x67), + 0x1D43B => array(0x68), + 0x1D43C => array(0x69), + 0x1D43D => array(0x6A), + 0x1D43E => array(0x6B), + 0x1D43F => array(0x6C), + 0x1D440 => array(0x6D), + 0x1D441 => array(0x6E), + 0x1D442 => array(0x6F), + 0x1D443 => array(0x70), + 0x1D444 => array(0x71), + 0x1D445 => array(0x72), + 0x1D446 => array(0x73), + 0x1D447 => array(0x74), + 0x1D448 => array(0x75), + 0x1D449 => array(0x76), + 0x1D44A => array(0x77), + 0x1D44B => array(0x78), + 0x1D44C => array(0x79), + 0x1D44D => array(0x7A), + 0x1D468 => array(0x61), + 0x1D469 => array(0x62), + 0x1D46A => array(0x63), + 0x1D46B => array(0x64), + 0x1D46C => array(0x65), + 0x1D46D => array(0x66), + 0x1D46E => array(0x67), + 0x1D46F => array(0x68), + 0x1D470 => array(0x69), + 0x1D471 => array(0x6A), + 0x1D472 => array(0x6B), + 0x1D473 => array(0x6C), + 0x1D474 => array(0x6D), + 0x1D475 => array(0x6E), + 0x1D476 => array(0x6F), + 0x1D477 => array(0x70), + 0x1D478 => array(0x71), + 0x1D479 => array(0x72), + 0x1D47A => array(0x73), + 0x1D47B => array(0x74), + 0x1D47C => array(0x75), + 0x1D47D => array(0x76), + 0x1D47E => array(0x77), + 0x1D47F => array(0x78), + 0x1D480 => array(0x79), + 0x1D481 => array(0x7A), + 0x1D49C => array(0x61), + 0x1D49E => array(0x63), + 0x1D49F => array(0x64), + 0x1D4A2 => array(0x67), + 0x1D4A5 => array(0x6A), + 0x1D4A6 => array(0x6B), + 0x1D4A9 => array(0x6E), + 0x1D4AA => array(0x6F), + 0x1D4AB => array(0x70), + 0x1D4AC => array(0x71), + 0x1D4AE => array(0x73), + 0x1D4AF => array(0x74), + 0x1D4B0 => array(0x75), + 0x1D4B1 => array(0x76), + 0x1D4B2 => array(0x77), + 0x1D4B3 => array(0x78), + 0x1D4B4 => array(0x79), + 0x1D4B5 => array(0x7A), + 0x1D4D0 => array(0x61), + 0x1D4D1 => array(0x62), + 0x1D4D2 => array(0x63), + 0x1D4D3 => array(0x64), + 0x1D4D4 => array(0x65), + 0x1D4D5 => array(0x66), + 0x1D4D6 => array(0x67), + 0x1D4D7 => array(0x68), + 0x1D4D8 => array(0x69), + 0x1D4D9 => array(0x6A), + 0x1D4DA => array(0x6B), + 0x1D4DB => array(0x6C), + 0x1D4DC => array(0x6D), + 0x1D4DD => array(0x6E), + 0x1D4DE => array(0x6F), + 0x1D4DF => array(0x70), + 0x1D4E0 => array(0x71), + 0x1D4E1 => array(0x72), + 0x1D4E2 => array(0x73), + 0x1D4E3 => array(0x74), + 0x1D4E4 => array(0x75), + 0x1D4E5 => array(0x76), + 0x1D4E6 => array(0x77), + 0x1D4E7 => array(0x78), + 0x1D4E8 => array(0x79), + 0x1D4E9 => array(0x7A), + 0x1D504 => array(0x61), + 0x1D505 => array(0x62), + 0x1D507 => array(0x64), + 0x1D508 => array(0x65), + 0x1D509 => array(0x66), + 0x1D50A => array(0x67), + 0x1D50D => array(0x6A), + 0x1D50E => array(0x6B), + 0x1D50F => array(0x6C), + 0x1D510 => array(0x6D), + 0x1D511 => array(0x6E), + 0x1D512 => array(0x6F), + 0x1D513 => array(0x70), + 0x1D514 => array(0x71), + 0x1D516 => array(0x73), + 0x1D517 => array(0x74), + 0x1D518 => array(0x75), + 0x1D519 => array(0x76), + 0x1D51A => array(0x77), + 0x1D51B => array(0x78), + 0x1D51C => array(0x79), + 0x1D538 => array(0x61), + 0x1D539 => array(0x62), + 0x1D53B => array(0x64), + 0x1D53C => array(0x65), + 0x1D53D => array(0x66), + 0x1D53E => array(0x67), + 0x1D540 => array(0x69), + 0x1D541 => array(0x6A), + 0x1D542 => array(0x6B), + 0x1D543 => array(0x6C), + 0x1D544 => array(0x6D), + 0x1D546 => array(0x6F), + 0x1D54A => array(0x73), + 0x1D54B => array(0x74), + 0x1D54C => array(0x75), + 0x1D54D => array(0x76), + 0x1D54E => array(0x77), + 0x1D54F => array(0x78), + 0x1D550 => array(0x79), + 0x1D56C => array(0x61), + 0x1D56D => array(0x62), + 0x1D56E => array(0x63), + 0x1D56F => array(0x64), + 0x1D570 => array(0x65), + 0x1D571 => array(0x66), + 0x1D572 => array(0x67), + 0x1D573 => array(0x68), + 0x1D574 => array(0x69), + 0x1D575 => array(0x6A), + 0x1D576 => array(0x6B), + 0x1D577 => array(0x6C), + 0x1D578 => array(0x6D), + 0x1D579 => array(0x6E), + 0x1D57A => array(0x6F), + 0x1D57B => array(0x70), + 0x1D57C => array(0x71), + 0x1D57D => array(0x72), + 0x1D57E => array(0x73), + 0x1D57F => array(0x74), + 0x1D580 => array(0x75), + 0x1D581 => array(0x76), + 0x1D582 => array(0x77), + 0x1D583 => array(0x78), + 0x1D584 => array(0x79), + 0x1D585 => array(0x7A), + 0x1D5A0 => array(0x61), + 0x1D5A1 => array(0x62), + 0x1D5A2 => array(0x63), + 0x1D5A3 => array(0x64), + 0x1D5A4 => array(0x65), + 0x1D5A5 => array(0x66), + 0x1D5A6 => array(0x67), + 0x1D5A7 => array(0x68), + 0x1D5A8 => array(0x69), + 0x1D5A9 => array(0x6A), + 0x1D5AA => array(0x6B), + 0x1D5AB => array(0x6C), + 0x1D5AC => array(0x6D), + 0x1D5AD => array(0x6E), + 0x1D5AE => array(0x6F), + 0x1D5AF => array(0x70), + 0x1D5B0 => array(0x71), + 0x1D5B1 => array(0x72), + 0x1D5B2 => array(0x73), + 0x1D5B3 => array(0x74), + 0x1D5B4 => array(0x75), + 0x1D5B5 => array(0x76), + 0x1D5B6 => array(0x77), + 0x1D5B7 => array(0x78), + 0x1D5B8 => array(0x79), + 0x1D5B9 => array(0x7A), + 0x1D5D4 => array(0x61), + 0x1D5D5 => array(0x62), + 0x1D5D6 => array(0x63), + 0x1D5D7 => array(0x64), + 0x1D5D8 => array(0x65), + 0x1D5D9 => array(0x66), + 0x1D5DA => array(0x67), + 0x1D5DB => array(0x68), + 0x1D5DC => array(0x69), + 0x1D5DD => array(0x6A), + 0x1D5DE => array(0x6B), + 0x1D5DF => array(0x6C), + 0x1D5E0 => array(0x6D), + 0x1D5E1 => array(0x6E), + 0x1D5E2 => array(0x6F), + 0x1D5E3 => array(0x70), + 0x1D5E4 => array(0x71), + 0x1D5E5 => array(0x72), + 0x1D5E6 => array(0x73), + 0x1D5E7 => array(0x74), + 0x1D5E8 => array(0x75), + 0x1D5E9 => array(0x76), + 0x1D5EA => array(0x77), + 0x1D5EB => array(0x78), + 0x1D5EC => array(0x79), + 0x1D5ED => array(0x7A), + 0x1D608 => array(0x61), + 0x1D609 => array(0x62), + 0x1D60A => array(0x63), + 0x1D60B => array(0x64), + 0x1D60C => array(0x65), + 0x1D60D => array(0x66), + 0x1D60E => array(0x67), + 0x1D60F => array(0x68), + 0x1D610 => array(0x69), + 0x1D611 => array(0x6A), + 0x1D612 => array(0x6B), + 0x1D613 => array(0x6C), + 0x1D614 => array(0x6D), + 0x1D615 => array(0x6E), + 0x1D616 => array(0x6F), + 0x1D617 => array(0x70), + 0x1D618 => array(0x71), + 0x1D619 => array(0x72), + 0x1D61A => array(0x73), + 0x1D61B => array(0x74), + 0x1D61C => array(0x75), + 0x1D61D => array(0x76), + 0x1D61E => array(0x77), + 0x1D61F => array(0x78), + 0x1D620 => array(0x79), + 0x1D621 => array(0x7A), + 0x1D63C => array(0x61), + 0x1D63D => array(0x62), + 0x1D63E => array(0x63), + 0x1D63F => array(0x64), + 0x1D640 => array(0x65), + 0x1D641 => array(0x66), + 0x1D642 => array(0x67), + 0x1D643 => array(0x68), + 0x1D644 => array(0x69), + 0x1D645 => array(0x6A), + 0x1D646 => array(0x6B), + 0x1D647 => array(0x6C), + 0x1D648 => array(0x6D), + 0x1D649 => array(0x6E), + 0x1D64A => array(0x6F), + 0x1D64B => array(0x70), + 0x1D64C => array(0x71), + 0x1D64D => array(0x72), + 0x1D64E => array(0x73), + 0x1D64F => array(0x74), + 0x1D650 => array(0x75), + 0x1D651 => array(0x76), + 0x1D652 => array(0x77), + 0x1D653 => array(0x78), + 0x1D654 => array(0x79), + 0x1D655 => array(0x7A), + 0x1D670 => array(0x61), + 0x1D671 => array(0x62), + 0x1D672 => array(0x63), + 0x1D673 => array(0x64), + 0x1D674 => array(0x65), + 0x1D675 => array(0x66), + 0x1D676 => array(0x67), + 0x1D677 => array(0x68), + 0x1D678 => array(0x69), + 0x1D679 => array(0x6A), + 0x1D67A => array(0x6B), + 0x1D67B => array(0x6C), + 0x1D67C => array(0x6D), + 0x1D67D => array(0x6E), + 0x1D67E => array(0x6F), + 0x1D67F => array(0x70), + 0x1D680 => array(0x71), + 0x1D681 => array(0x72), + 0x1D682 => array(0x73), + 0x1D683 => array(0x74), + 0x1D684 => array(0x75), + 0x1D685 => array(0x76), + 0x1D686 => array(0x77), + 0x1D687 => array(0x78), + 0x1D688 => array(0x79), + 0x1D689 => array(0x7A), + 0x1D6A8 => array(0x3B1), + 0x1D6A9 => array(0x3B2), + 0x1D6AA => array(0x3B3), + 0x1D6AB => array(0x3B4), + 0x1D6AC => array(0x3B5), + 0x1D6AD => array(0x3B6), + 0x1D6AE => array(0x3B7), + 0x1D6AF => array(0x3B8), + 0x1D6B0 => array(0x3B9), + 0x1D6B1 => array(0x3BA), + 0x1D6B2 => array(0x3BB), + 0x1D6B3 => array(0x3BC), + 0x1D6B4 => array(0x3BD), + 0x1D6B5 => array(0x3BE), + 0x1D6B6 => array(0x3BF), + 0x1D6B7 => array(0x3C0), + 0x1D6B8 => array(0x3C1), + 0x1D6B9 => array(0x3B8), + 0x1D6BA => array(0x3C3), + 0x1D6BB => array(0x3C4), + 0x1D6BC => array(0x3C5), + 0x1D6BD => array(0x3C6), + 0x1D6BE => array(0x3C7), + 0x1D6BF => array(0x3C8), + 0x1D6C0 => array(0x3C9), + 0x1D6D3 => array(0x3C3), + 0x1D6E2 => array(0x3B1), + 0x1D6E3 => array(0x3B2), + 0x1D6E4 => array(0x3B3), + 0x1D6E5 => array(0x3B4), + 0x1D6E6 => array(0x3B5), + 0x1D6E7 => array(0x3B6), + 0x1D6E8 => array(0x3B7), + 0x1D6E9 => array(0x3B8), + 0x1D6EA => array(0x3B9), + 0x1D6EB => array(0x3BA), + 0x1D6EC => array(0x3BB), + 0x1D6ED => array(0x3BC), + 0x1D6EE => array(0x3BD), + 0x1D6EF => array(0x3BE), + 0x1D6F0 => array(0x3BF), + 0x1D6F1 => array(0x3C0), + 0x1D6F2 => array(0x3C1), + 0x1D6F3 => array(0x3B8), + 0x1D6F4 => array(0x3C3), + 0x1D6F5 => array(0x3C4), + 0x1D6F6 => array(0x3C5), + 0x1D6F7 => array(0x3C6), + 0x1D6F8 => array(0x3C7), + 0x1D6F9 => array(0x3C8), + 0x1D6FA => array(0x3C9), + 0x1D70D => array(0x3C3), + 0x1D71C => array(0x3B1), + 0x1D71D => array(0x3B2), + 0x1D71E => array(0x3B3), + 0x1D71F => array(0x3B4), + 0x1D720 => array(0x3B5), + 0x1D721 => array(0x3B6), + 0x1D722 => array(0x3B7), + 0x1D723 => array(0x3B8), + 0x1D724 => array(0x3B9), + 0x1D725 => array(0x3BA), + 0x1D726 => array(0x3BB), + 0x1D727 => array(0x3BC), + 0x1D728 => array(0x3BD), + 0x1D729 => array(0x3BE), + 0x1D72A => array(0x3BF), + 0x1D72B => array(0x3C0), + 0x1D72C => array(0x3C1), + 0x1D72D => array(0x3B8), + 0x1D72E => array(0x3C3), + 0x1D72F => array(0x3C4), + 0x1D730 => array(0x3C5), + 0x1D731 => array(0x3C6), + 0x1D732 => array(0x3C7), + 0x1D733 => array(0x3C8), + 0x1D734 => array(0x3C9), + 0x1D747 => array(0x3C3), + 0x1D756 => array(0x3B1), + 0x1D757 => array(0x3B2), + 0x1D758 => array(0x3B3), + 0x1D759 => array(0x3B4), + 0x1D75A => array(0x3B5), + 0x1D75B => array(0x3B6), + 0x1D75C => array(0x3B7), + 0x1D75D => array(0x3B8), + 0x1D75E => array(0x3B9), + 0x1D75F => array(0x3BA), + 0x1D760 => array(0x3BB), + 0x1D761 => array(0x3BC), + 0x1D762 => array(0x3BD), + 0x1D763 => array(0x3BE), + 0x1D764 => array(0x3BF), + 0x1D765 => array(0x3C0), + 0x1D766 => array(0x3C1), + 0x1D767 => array(0x3B8), + 0x1D768 => array(0x3C3), + 0x1D769 => array(0x3C4), + 0x1D76A => array(0x3C5), + 0x1D76B => array(0x3C6), + 0x1D76C => array(0x3C7), + 0x1D76D => array(0x3C8), + 0x1D76E => array(0x3C9), + 0x1D781 => array(0x3C3), + 0x1D790 => array(0x3B1), + 0x1D791 => array(0x3B2), + 0x1D792 => array(0x3B3), + 0x1D793 => array(0x3B4), + 0x1D794 => array(0x3B5), + 0x1D795 => array(0x3B6), + 0x1D796 => array(0x3B7), + 0x1D797 => array(0x3B8), + 0x1D798 => array(0x3B9), + 0x1D799 => array(0x3BA), + 0x1D79A => array(0x3BB), + 0x1D79B => array(0x3BC), + 0x1D79C => array(0x3BD), + 0x1D79D => array(0x3BE), + 0x1D79E => array(0x3BF), + 0x1D79F => array(0x3C0), + 0x1D7A0 => array(0x3C1), + 0x1D7A1 => array(0x3B8), + 0x1D7A2 => array(0x3C3), + 0x1D7A3 => array(0x3C4), + 0x1D7A4 => array(0x3C5), + 0x1D7A5 => array(0x3C6), + 0x1D7A6 => array(0x3C7), + 0x1D7A7 => array(0x3C8), + 0x1D7A8 => array(0x3C9), + 0x1D7BB => array(0x3C3), + 0x3F9 => array(0x3C3), + 0x1D2C => array(0x61), + 0x1D2D => array(0xE6), + 0x1D2E => array(0x62), + 0x1D30 => array(0x64), + 0x1D31 => array(0x65), + 0x1D32 => array(0x1DD), + 0x1D33 => array(0x67), + 0x1D34 => array(0x68), + 0x1D35 => array(0x69), + 0x1D36 => array(0x6A), + 0x1D37 => array(0x6B), + 0x1D38 => array(0x6C), + 0x1D39 => array(0x6D), + 0x1D3A => array(0x6E), + 0x1D3C => array(0x6F), + 0x1D3D => array(0x223), + 0x1D3E => array(0x70), + 0x1D3F => array(0x72), + 0x1D40 => array(0x74), + 0x1D41 => array(0x75), + 0x1D42 => array(0x77), + 0x213B => array(0x66, 0x61, 0x78), + 0x3250 => array(0x70, 0x74, 0x65), + 0x32CC => array(0x68, 0x67), + 0x32CE => array(0x65, 0x76), + 0x32CF => array(0x6C, 0x74, 0x64), + 0x337A => array(0x69, 0x75), + 0x33DE => array(0x76, 0x2215, 0x6D), + 0x33DF => array(0x61, 0x2215, 0x6D) + ); + + /** + * Normalization Combining Classes; Code Points not listed + * got Combining Class 0. + * + * @static + * @var array + * @access private + */ + private static $_np_norm_combcls = array( + 0x334 => 1, + 0x335 => 1, + 0x336 => 1, + 0x337 => 1, + 0x338 => 1, + 0x93C => 7, + 0x9BC => 7, + 0xA3C => 7, + 0xABC => 7, + 0xB3C => 7, + 0xCBC => 7, + 0x1037 => 7, + 0x3099 => 8, + 0x309A => 8, + 0x94D => 9, + 0x9CD => 9, + 0xA4D => 9, + 0xACD => 9, + 0xB4D => 9, + 0xBCD => 9, + 0xC4D => 9, + 0xCCD => 9, + 0xD4D => 9, + 0xDCA => 9, + 0xE3A => 9, + 0xF84 => 9, + 0x1039 => 9, + 0x1714 => 9, + 0x1734 => 9, + 0x17D2 => 9, + 0x5B0 => 10, + 0x5B1 => 11, + 0x5B2 => 12, + 0x5B3 => 13, + 0x5B4 => 14, + 0x5B5 => 15, + 0x5B6 => 16, + 0x5B7 => 17, + 0x5B8 => 18, + 0x5B9 => 19, + 0x5BB => 20, + 0x5Bc => 21, + 0x5BD => 22, + 0x5BF => 23, + 0x5C1 => 24, + 0x5C2 => 25, + 0xFB1E => 26, + 0x64B => 27, + 0x64C => 28, + 0x64D => 29, + 0x64E => 30, + 0x64F => 31, + 0x650 => 32, + 0x651 => 33, + 0x652 => 34, + 0x670 => 35, + 0x711 => 36, + 0xC55 => 84, + 0xC56 => 91, + 0xE38 => 103, + 0xE39 => 103, + 0xE48 => 107, + 0xE49 => 107, + 0xE4A => 107, + 0xE4B => 107, + 0xEB8 => 118, + 0xEB9 => 118, + 0xEC8 => 122, + 0xEC9 => 122, + 0xECA => 122, + 0xECB => 122, + 0xF71 => 129, + 0xF72 => 130, + 0xF7A => 130, + 0xF7B => 130, + 0xF7C => 130, + 0xF7D => 130, + 0xF80 => 130, + 0xF74 => 132, + 0x321 => 202, + 0x322 => 202, + 0x327 => 202, + 0x328 => 202, + 0x31B => 216, + 0xF39 => 216, + 0x1D165 => 216, + 0x1D166 => 216, + 0x1D16E => 216, + 0x1D16F => 216, + 0x1D170 => 216, + 0x1D171 => 216, + 0x1D172 => 216, + 0x302A => 218, + 0x316 => 220, + 0x317 => 220, + 0x318 => 220, + 0x319 => 220, + 0x31C => 220, + 0x31D => 220, + 0x31E => 220, + 0x31F => 220, + 0x320 => 220, + 0x323 => 220, + 0x324 => 220, + 0x325 => 220, + 0x326 => 220, + 0x329 => 220, + 0x32A => 220, + 0x32B => 220, + 0x32C => 220, + 0x32D => 220, + 0x32E => 220, + 0x32F => 220, + 0x330 => 220, + 0x331 => 220, + 0x332 => 220, + 0x333 => 220, + 0x339 => 220, + 0x33A => 220, + 0x33B => 220, + 0x33C => 220, + 0x347 => 220, + 0x348 => 220, + 0x349 => 220, + 0x34D => 220, + 0x34E => 220, + 0x353 => 220, + 0x354 => 220, + 0x355 => 220, + 0x356 => 220, + 0x591 => 220, + 0x596 => 220, + 0x59B => 220, + 0x5A3 => 220, + 0x5A4 => 220, + 0x5A5 => 220, + 0x5A6 => 220, + 0x5A7 => 220, + 0x5AA => 220, + 0x655 => 220, + 0x656 => 220, + 0x6E3 => 220, + 0x6EA => 220, + 0x6ED => 220, + 0x731 => 220, + 0x734 => 220, + 0x737 => 220, + 0x738 => 220, + 0x739 => 220, + 0x73B => 220, + 0x73C => 220, + 0x73E => 220, + 0x742 => 220, + 0x744 => 220, + 0x746 => 220, + 0x748 => 220, + 0x952 => 220, + 0xF18 => 220, + 0xF19 => 220, + 0xF35 => 220, + 0xF37 => 220, + 0xFC6 => 220, + 0x193B => 220, + 0x20E8 => 220, + 0x1D17B => 220, + 0x1D17C => 220, + 0x1D17D => 220, + 0x1D17E => 220, + 0x1D17F => 220, + 0x1D180 => 220, + 0x1D181 => 220, + 0x1D182 => 220, + 0x1D18A => 220, + 0x1D18B => 220, + 0x59A => 222, + 0x5AD => 222, + 0x1929 => 222, + 0x302D => 222, + 0x302E => 224, + 0x302F => 224, + 0x1D16D => 226, + 0x5AE => 228, + 0x18A9 => 228, + 0x302B => 228, + 0x300 => 230, + 0x301 => 230, + 0x302 => 230, + 0x303 => 230, + 0x304 => 230, + 0x305 => 230, + 0x306 => 230, + 0x307 => 230, + 0x308 => 230, + 0x309 => 230, + 0x30A => 230, + 0x30B => 230, + 0x30C => 230, + 0x30D => 230, + 0x30E => 230, + 0x30F => 230, + 0x310 => 230, + 0x311 => 230, + 0x312 => 230, + 0x313 => 230, + 0x314 => 230, + 0x33D => 230, + 0x33E => 230, + 0x33F => 230, + 0x340 => 230, + 0x341 => 230, + 0x342 => 230, + 0x343 => 230, + 0x344 => 230, + 0x346 => 230, + 0x34A => 230, + 0x34B => 230, + 0x34C => 230, + 0x350 => 230, + 0x351 => 230, + 0x352 => 230, + 0x357 => 230, + 0x363 => 230, + 0x364 => 230, + 0x365 => 230, + 0x366 => 230, + 0x367 => 230, + 0x368 => 230, + 0x369 => 230, + 0x36A => 230, + 0x36B => 230, + 0x36C => 230, + 0x36D => 230, + 0x36E => 230, + 0x36F => 230, + 0x483 => 230, + 0x484 => 230, + 0x485 => 230, + 0x486 => 230, + 0x592 => 230, + 0x593 => 230, + 0x594 => 230, + 0x595 => 230, + 0x597 => 230, + 0x598 => 230, + 0x599 => 230, + 0x59C => 230, + 0x59D => 230, + 0x59E => 230, + 0x59F => 230, + 0x5A0 => 230, + 0x5A1 => 230, + 0x5A8 => 230, + 0x5A9 => 230, + 0x5AB => 230, + 0x5AC => 230, + 0x5AF => 230, + 0x5C4 => 230, + 0x610 => 230, + 0x611 => 230, + 0x612 => 230, + 0x613 => 230, + 0x614 => 230, + 0x615 => 230, + 0x653 => 230, + 0x654 => 230, + 0x657 => 230, + 0x658 => 230, + 0x6D6 => 230, + 0x6D7 => 230, + 0x6D8 => 230, + 0x6D9 => 230, + 0x6DA => 230, + 0x6DB => 230, + 0x6DC => 230, + 0x6DF => 230, + 0x6E0 => 230, + 0x6E1 => 230, + 0x6E2 => 230, + 0x6E4 => 230, + 0x6E7 => 230, + 0x6E8 => 230, + 0x6EB => 230, + 0x6EC => 230, + 0x730 => 230, + 0x732 => 230, + 0x733 => 230, + 0x735 => 230, + 0x736 => 230, + 0x73A => 230, + 0x73D => 230, + 0x73F => 230, + 0x740 => 230, + 0x741 => 230, + 0x743 => 230, + 0x745 => 230, + 0x747 => 230, + 0x749 => 230, + 0x74A => 230, + 0x951 => 230, + 0x953 => 230, + 0x954 => 230, + 0xF82 => 230, + 0xF83 => 230, + 0xF86 => 230, + 0xF87 => 230, + 0x170D => 230, + 0x193A => 230, + 0x20D0 => 230, + 0x20D1 => 230, + 0x20D4 => 230, + 0x20D5 => 230, + 0x20D6 => 230, + 0x20D7 => 230, + 0x20DB => 230, + 0x20DC => 230, + 0x20E1 => 230, + 0x20E7 => 230, + 0x20E9 => 230, + 0xFE20 => 230, + 0xFE21 => 230, + 0xFE22 => 230, + 0xFE23 => 230, + 0x1D185 => 230, + 0x1D186 => 230, + 0x1D187 => 230, + 0x1D189 => 230, + 0x1D188 => 230, + 0x1D1AA => 230, + 0x1D1AB => 230, + 0x1D1AC => 230, + 0x1D1AD => 230, + 0x315 => 232, + 0x31A => 232, + 0x302C => 232, + 0x35F => 233, + 0x362 => 233, + 0x35D => 234, + 0x35E => 234, + 0x360 => 234, + 0x361 => 234, + 0x345 => 240 + ); + // }}} + + // {{{ properties + /** + * @var string + * @access private + */ + private $_punycode_prefix = 'xn--'; + + /** + * @access private + */ + private $_invalid_ucs = 0x80000000; + + /** + * @access private + */ + private $_max_ucs = 0x10FFFF; + + /** + * @var int + * @access private + */ + private $_base = 36; + + /** + * @var int + * @access private + */ + private $_tmin = 1; + + /** + * @var int + * @access private + */ + private $_tmax = 26; + + /** + * @var int + * @access private + */ + private $_skew = 38; + + /** + * @var int + * @access private + */ + private $_damp = 700; + + /** + * @var int + * @access private + */ + private $_initial_bias = 72; + + /** + * @var int + * @access private + */ + private $_initial_n = 0x80; + + /** + * @var int + * @access private + */ + private $_slast; + + /** + * @access private + */ + private $_sbase = 0xAC00; + + /** + * @access private + */ + private $_lbase = 0x1100; + + /** + * @access private + */ + private $_vbase = 0x1161; + + /** + * @access private + */ + private $_tbase = 0x11a7; + + /** + * @var int + * @access private + */ + private $_lcount = 19; + + /** + * @var int + * @access private + */ + private $_vcount = 21; + + /** + * @var int + * @access private + */ + private $_tcount = 28; + + /** + * vcount * tcount + * + * @var int + * @access private + */ + private $_ncount = 588; + + /** + * lcount * tcount * vcount + * + * @var int + * @access private + */ + private $_scount = 11172; + + /** + * Default encoding for encode()'s input and decode()'s output is UTF-8; + * Other possible encodings are ucs4_string and ucs4_array + * See {@link setParams()} for how to select these + * + * @var bool + * @access private + */ + private $_api_encoding = 'utf8'; + + /** + * Overlong UTF-8 encodings are forbidden + * + * @var bool + * @access private + */ + private $_allow_overlong = false; + + /** + * Behave strict or not + * + * @var bool + * @access private + */ + private $_strict_mode = false; + // }}} + + + // {{{ constructor + /** + * Constructor + * + * @param array $options + * @access public + * @see setParams() + */ + public function __construct($options = null) + { + $this->_slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; + + if (is_array($options)) { + $this->setParams($options); + } + } + // }}} + + + /** + * Sets a new option value. Available options and values: + * + * [utf8 - Use either UTF-8 or ISO-8859-1 as input (true for UTF-8, false + * otherwise); The output is always UTF-8] + * [overlong - Unicode does not allow unnecessarily long encodings of chars, + * to allow this, set this parameter to true, else to false; + * default is false.] + * [strict - true: strict mode, good for registration purposes - Causes errors + * on failures; false: loose mode, ideal for "wildlife" applications + * by silently ignoring errors and returning the original input instead] + * + * @param mixed $option Parameter to set (string: single parameter; array of Parameter => Value pairs) + * @param string $value Value to use (if parameter 1 is a string) + * @return boolean true on success, false otherwise + * @access public + */ + public function setParams($option, $value = false) + { + if (!is_array($option)) { + $option = array($option => $value); + } + + foreach ($option as $k => $v) { + switch ($k) { + case 'encoding': + switch ($v) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + $this->_api_encoding = $v; + break; + + default: + throw new Exception('Set Parameter: Unknown parameter '.$v.' for option '.$k); + } + + break; + + case 'overlong': + $this->_allow_overlong = ($v) ? true : false; + break; + + case 'strict': + $this->_strict_mode = ($v) ? true : false; + break; + + default: + return false; + } + } + + return true; + } + + /** + * Encode a given UTF-8 domain name. + * + * @param string $decoded Domain name (UTF-8 or UCS-4) + * [@param string $encoding Desired input encoding, see {@link set_parameter}] + * @return string Encoded Domain name (ACE string) + * @return mixed processed string + * @throws Exception + * @access public + */ + public function encode($decoded, $one_time_encoding = false) + { + // Forcing conversion of input to UCS4 array + // If one time encoding is given, use this, else the objects property + switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + $decoded = $this->_utf8_to_ucs4($decoded); + break; + case 'ucs4_string': + $decoded = $this->_ucs4_string_to_ucs4($decoded); + case 'ucs4_array': // No break; before this line. Catch case, but do nothing + break; + default: + throw new Exception('Unsupported input format'); + } + + // No input, no output, what else did you expect? + if (empty($decoded)) return ''; + + // Anchors for iteration + $last_begin = 0; + // Output string + $output = ''; + + foreach ($decoded as $k => $v) { + // Make sure to use just the plain dot + switch($v) { + case 0x3002: + case 0xFF0E: + case 0xFF61: + $decoded[$k] = 0x2E; + // It's right, no break here + // The codepoints above have to be converted to dots anyway + + // Stumbling across an anchoring character + case 0x2E: + case 0x2F: + case 0x3A: + case 0x3F: + case 0x40: + // Neither email addresses nor URLs allowed in strict mode + if ($this->_strict_mode) { + throw new Exception('Neither email addresses nor URLs are allowed in strict mode.'); + } else { + // Skip first char + if ($k) { + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); + } + $output .= chr($decoded[$k]); + } + $last_begin = $k + 1; + } + } + } + // Catch the rest of the string + if ($last_begin) { + $inp_len = sizeof($decoded); + $encoded = ''; + $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); + if ($encoded) { + $output .= $encoded; + } else { + $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); + } + return $output; + } else { + if ($output = $this->_encode($decoded)) { + return $output; + } else { + return $this->_ucs4_to_utf8($decoded); + } + } + } + + /** + * Decode a given ACE domain name. + * + * @param string $encoded Domain name (ACE string) + * @param string $encoding Desired output encoding, see {@link set_parameter} + * @return string Decoded Domain name (UTF-8 or UCS-4) + * @throws Exception + * @access public + */ + public function decode($input, $one_time_encoding = false) + { + // Optionally set + if ($one_time_encoding) { + switch ($one_time_encoding) { + case 'utf8': + case 'ucs4_string': + case 'ucs4_array': + break; + default: + throw new Exception('Unknown encoding '.$one_time_encoding); + return false; + } + } + // Make sure to drop any newline characters around + $input = trim($input); + + // Negotiate input and try to determine, wether it is a plain string, + // an email address or something like a complete URL + if (strpos($input, '@')) { // Maybe it is an email address + // No no in strict mode + if ($this->_strict_mode) { + throw new Exception('Only simple domain name parts can be handled in strict mode'); + } + list($email_pref, $input) = explode('@', $input, 2); + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $return = $email_pref . '@' . join('.', $arr); + } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) + // No no in strict mode + if ($this->_strict_mode) { + throw new Exception('Only simple domain name parts can be handled in strict mode'); + } + $parsed = parse_url($input); + if (isset($parsed['host'])) { + $arr = explode('.', $parsed['host']); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $parsed['host'] = join('.', $arr); + if (isset($parsed['scheme'])) { + $parsed['scheme'] .= (strtolower($parsed['scheme']) == 'mailto') ? ':' : '://'; + } + $return = join('', $parsed); + } else { // parse_url seems to have failed, try without it + $arr = explode('.', $input); + foreach ($arr as $k => $v) { + $conv = $this->_decode($v); + if ($conv) $arr[$k] = $conv; + } + $return = join('.', $arr); + } + } else { // Otherwise we consider it being a pure domain name string + $return = $this->_decode($input); + } + // The output is UTF-8 by default, other output formats need conversion here + // If one time encoding is given, use this, else the objects property + switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { + case 'utf8': + return $return; + break; + case 'ucs4_string': + return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); + break; + case 'ucs4_array': + return $this->_utf8_to_ucs4($return); + break; + default: + throw new Exception('Unsupported output format'); + } + } + + + // {{{ private + /** + * The actual encoding algorithm. + * + * @return string + * @throws Exception + * @access private + */ + private function _encode($decoded) + { + // We cannot encode a domain name containing the Punycode prefix + $extract = strlen($this->_punycode_prefix); + $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); + $check_deco = array_slice($decoded, 0, $extract); + + if ($check_pref == $check_deco) { + throw new Exception('This is already a punycode string'); + } + // We will not try to encode strings consisting of basic code points only + $encodable = false; + foreach ($decoded as $k => $v) { + if ($v > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + if ($this->_strict_mode) { + throw new Exception('The given string does not contain encodable chars'); + } else { + return false; + } + } + + // Do NAMEPREP + try { + $decoded = $this->_nameprep($decoded); + } catch (Exception $e) { + // hmm, serious - rethrow + throw $e; + } + + $deco_len = count($decoded); + + // Empty array + if (!$deco_len) { + return false; + } + + // How many chars have been consumed + $codecount = 0; + + // Start with the prefix; copy it to output + $encoded = $this->_punycode_prefix; + + $encoded = ''; + // Copy all basic code points to output + for ($i = 0; $i < $deco_len; ++$i) { + $test = $decoded[$i]; + // Will match [0-9a-zA-Z-] + if ((0x2F < $test && $test < 0x40) + || (0x40 < $test && $test < 0x5B) + || (0x60 < $test && $test <= 0x7B) + || (0x2D == $test)) { + $encoded .= chr($decoded[$i]); + $codecount++; + } + } + + // All codepoints were basic ones + if ($codecount == $deco_len) { + return $encoded; + } + + // Start with the prefix; copy it to output + $encoded = $this->_punycode_prefix . $encoded; + + // If we have basic code points in output, add an hyphen to the end + if ($codecount) { + $encoded .= '-'; + } + + // Now find and encode all non-basic code points + $is_first = true; + $cur_code = $this->_initial_n; + $bias = $this->_initial_bias; + $delta = 0; + + while ($codecount < $deco_len) { + // Find the smallest code point >= the current code point and + // remember the last ouccrence of it in the input + for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { + if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { + $next_code = $decoded[$i]; + } + } + + $delta += ($next_code - $cur_code) * ($codecount + 1); + $cur_code = $next_code; + + // Scan input again and encode all characters whose code point is $cur_code + for ($i = 0; $i < $deco_len; $i++) { + if ($decoded[$i] < $cur_code) { + $delta++; + } else if ($decoded[$i] == $cur_code) { + for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { + $t = ($k <= $bias)? + $this->_tmin : + (($k >= $bias + $this->_tmax)? $this->_tmax : $k - $bias); + + if ($q < $t) { + break; + } + + $encoded .= $this->_encodeDigit(ceil($t + (($q - $t) % ($this->_base - $t)))); + $q = ($q - $t) / ($this->_base - $t); + } + + $encoded .= $this->_encodeDigit($q); + $bias = $this->_adapt($delta, $codecount + 1, $is_first); + $codecount++; + $delta = 0; + $is_first = false; + } + } + + $delta++; + $cur_code++; + } + + return $encoded; + } + + /** + * The actual decoding algorithm. + * + * @return string + * @throws Exception + * @access private + */ + private function _decode($encoded) + { + // We do need to find the Punycode prefix + if (!preg_match('!^' . preg_quote($this->_punycode_prefix, '!') . '!', $encoded)) { + return false; + } + + $encode_test = preg_replace('!^' . preg_quote($this->_punycode_prefix, '!') . '!', '', $encoded); + + // If nothing left after removing the prefix, it is hopeless + if (!$encode_test) { + return false; + } + + // Find last occurence of the delimiter + $delim_pos = strrpos($encoded, '-'); + + if ($delim_pos > strlen($this->_punycode_prefix)) { + for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) { + $decoded[] = ord($encoded{$k}); + } + } else { + $decoded = array(); + } + + $deco_len = count($decoded); + $enco_len = strlen($encoded); + + // Wandering through the strings; init + $is_first = true; + $bias = $this->_initial_bias; + $idx = 0; + $char = $this->_initial_n; + + for ($enco_idx = ($delim_pos)? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { + for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { + $digit = $this->_decodeDigit($encoded{$enco_idx++}); + $idx += $digit * $w; + + $t = ($k <= $bias) ? + $this->_tmin : + (($k >= $bias + $this->_tmax)? $this->_tmax : ($k - $bias)); + + if ($digit < $t) { + break; + } + + $w = (int)($w * ($this->_base - $t)); + } + + $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); + $is_first = false; + $char += (int) ($idx / ($deco_len + 1)); + $idx %= ($deco_len + 1); + + if ($deco_len > 0) { + // Make room for the decoded char + for ($i = $deco_len; $i > $idx; $i--) { + $decoded[$i] = $decoded[($i - 1)]; + } + } + + $decoded[$idx++] = $char; + } + + try { + return $this->_ucs4_to_utf8($decoded); + } catch (Exception $e) { + // rethrow + throw $e; + } + } + + /** + * Adapt the bias according to the current code point and position. + * + * @access private + */ + private function _adapt($delta, $npoints, $is_first) + { + $delta = (int) ($is_first ? ($delta / $this->_damp) : ($delta / 2)); + $delta += (int) ($delta / $npoints); + + for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { + $delta = (int) ($delta / ($this->_base - $this->_tmin)); + } + + return (int) ($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); + } + + /** + * Encoding a certain digit. + * + * @access private + */ + private function _encodeDigit($d) + { + return chr($d + 22 + 75 * ($d < 26)); + } + + /** + * Decode a certain digit. + * + * @access private + */ + private function _decodeDigit($cp) + { + $cp = ord($cp); + return ($cp - 48 < 10)? $cp - 22 : (($cp - 65 < 26)? $cp - 65 : (($cp - 97 < 26)? $cp - 97 : $this->_base)); + } + + /** + * Do Nameprep according to RFC3491 and RFC3454. + * + * @param array $input Unicode Characters + * @return string Unicode Characters, Nameprep'd + * @throws Exception + * @access private + */ + private function _nameprep($input) + { + $output = array(); + + // Walking through the input array, performing the required steps on each of + // the input chars and putting the result into the output array + // While mapping required chars we apply the cannonical ordering + + foreach ($input as $v) { + // Map to nothing == skip that code point + if (in_array($v, self::$_np_map_nothing)) { + continue; + } + + // Try to find prohibited input + if (in_array($v, self::$_np_prohibit) || in_array($v, self::$_general_prohibited)) { + throw new Exception('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v)); + } + + foreach (self::$_np_prohibit_ranges as $range) { + if ($range[0] <= $v && $v <= $range[1]) { + throw new Exception('NAMEPREP: Prohibited input U+' . sprintf('%08X', $v)); + } + } + + // Hangul syllable decomposition + if (0xAC00 <= $v && $v <= 0xD7AF) { + foreach ($this->_hangulDecompose($v) as $out) { + $output[] = $out; + } + } else if (isset(self::$_np_replacemaps[$v])) { // There's a decomposition mapping for that code point + foreach ($this->_applyCannonicalOrdering(self::$_np_replacemaps[$v]) as $out) { + $output[] = $out; + } + } else { + $output[] = $v; + } + } + + // Combine code points + + $last_class = 0; + $last_starter = 0; + $out_len = count($output); + + for ($i = 0; $i < $out_len; ++$i) { + $class = $this->_getCombiningClass($output[$i]); + + if ((!$last_class || $last_class != $class) && $class) { + // Try to match + $seq_len = $i - $last_starter; + $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); + + // On match: Replace the last starter with the composed character and remove + // the now redundant non-starter(s) + if ($out) { + $output[$last_starter] = $out; + + if (count($out) != $seq_len) { + for ($j = $i + 1; $j < $out_len; ++$j) { + $output[$j - 1] = $output[$j]; + } + + unset($output[$out_len]); + } + + // Rewind the for loop by one, since there can be more possible compositions + $i--; + $out_len--; + $last_class = ($i == $last_starter)? 0 : $this->_getCombiningClass($output[$i - 1]); + + continue; + } + } + + // The current class is 0 + if (!$class) { + $last_starter = $i; + } + + $last_class = $class; + } + + return $output; + } + + /** + * Decomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul). + * + * @param integer $char 32bit UCS4 code point + * @return array Either Hangul Syllable decomposed or original 32bit + * value as one value array + * @access private + */ + private function _hangulDecompose($char) + { + $sindex = $char - $this->_sbase; + + if ($sindex < 0 || $sindex >= $this->_scount) { + return array($char); + } + + $result = array(); + $T = $this->_tbase + $sindex % $this->_tcount; + $result[] = (int)($this->_lbase + $sindex / $this->_ncount); + $result[] = (int)($this->_vbase + ($sindex % $this->_ncount) / $this->_tcount); + + if ($T != $this->_tbase) { + $result[] = $T; + } + + return $result; + } + + /** + * Ccomposes a Hangul syllable + * (see http://www.unicode.org/unicode/reports/tr15/#Hangul). + * + * @param array $input Decomposed UCS4 sequence + * @return array UCS4 sequence with syllables composed + * @access private + */ + private function _hangulCompose($input) + { + $inp_len = count($input); + + if (!$inp_len) { + return array(); + } + + $result = array(); + $last = $input[0]; + $result[] = $last; // copy first char from input to output + + for ($i = 1; $i < $inp_len; ++$i) { + $char = $input[$i]; + + // Find out, wether two current characters from L and V + $lindex = $last - $this->_lbase; + + if (0 <= $lindex && $lindex < $this->_lcount) { + $vindex = $char - $this->_vbase; + + if (0 <= $vindex && $vindex < $this->_vcount) { + // create syllable of form LV + $last = ($this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount); + $out_off = count($result) - 1; + $result[$out_off] = $last; // reset last + + // discard char + continue; + } + } + + // Find out, wether two current characters are LV and T + $sindex = $last - $this->_sbase; + + if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount) == 0) { + $tindex = $char - $this->_tbase; + + if (0 <= $tindex && $tindex <= $this->_tcount) { + // create syllable of form LVT + $last += $tindex; + $out_off = count($result) - 1; + $result[$out_off] = $last; // reset last + + // discard char + continue; + } + } + + // if neither case was true, just add the character + $last = $char; + $result[] = $char; + } + + return $result; + } + + /** + * Returns the combining class of a certain wide char. + * + * @param integer $char Wide char to check (32bit integer) + * @return integer Combining class if found, else 0 + * @access private + */ + private function _getCombiningClass($char) + { + return isset(self::$_np_norm_combcls[$char])? self::$_np_norm_combcls[$char] : 0; + } + + /** + * Apllies the cannonical ordering of a decomposed UCS4 sequence. + * + * @param array $input Decomposed UCS4 sequence + * @return array Ordered USC4 sequence + * @access private + */ + private function _applyCannonicalOrdering($input) + { + $swap = true; + $size = count($input); + + while ($swap) { + $swap = false; + $last = $this->_getCombiningClass($input[0]); + + for ($i = 0; $i < $size - 1; ++$i) { + $next = $this->_getCombiningClass($input[$i + 1]); + + if ($next != 0 && $last > $next) { + // Move item leftward until it fits + for ($j = $i + 1; $j > 0; --$j) { + if ($this->_getCombiningClass($input[$j - 1]) <= $next) { + break; + } + + $t = $input[$j]; + $input[$j] = $input[$j - 1]; + $input[$j - 1] = $t; + $swap = 1; + } + + // Reentering the loop looking at the old character again + $next = $last; + } + + $last = $next; + } + } + + return $input; + } + + /** + * Do composition of a sequence of starter and non-starter. + * + * @param array $input UCS4 Decomposed sequence + * @return array Ordered USC4 sequence + * @access private + */ + private function _combine($input) + { + $inp_len = count($input); + + // Is it a Hangul syllable? + if (1 != $inp_len) { + $hangul = $this->_hangulCompose($input); + + // This place is probably wrong + if (count($hangul) != $inp_len) { + return $hangul; + } + } + + foreach (self::$_np_replacemaps as $np_src => $np_target) { + if ($np_target[0] != $input[0]) { + continue; + } + + if (count($np_target) != $inp_len) { + continue; + } + + $hit = false; + + foreach ($input as $k2 => $v2) { + if ($v2 == $np_target[$k2]) { + $hit = true; + } else { + $hit = false; + break; + } + } + + if ($hit) { + return $np_src; + } + } + + return false; + } + + /** + * This converts an UTF-8 encoded string to its UCS-4 (array) representation + * By talking about UCS-4 we mean arrays of 32bit integers representing + * each of the "chars". This is due to PHP not being able to handle strings with + * bit depth different from 8. This applies to the reverse method _ucs4_to_utf8(), too. + * The following UTF-8 encodings are supported: + * + * bytes bits representation + * 1 7 0xxxxxxx + * 2 11 110xxxxx 10xxxxxx + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * + * Each x represents a bit that can be used to store character data. + * + * @access private + */ + private function _utf8_to_ucs4($input) + { + $output = array(); + $out_len = 0; + $inp_len = strlen($input); + $mode = 'next'; + $test = 'none'; + for ($k = 0; $k < $inp_len; ++$k) { + $v = ord($input{$k}); // Extract byte from input string + + if ($v < 128) { // We found an ASCII char - put into stirng as is + $output[$out_len] = $v; + ++$out_len; + if ('add' == $mode) { + throw new Exception('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); + return false; + } + continue; + } + if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char + $start_byte = $v; + $mode = 'add'; + $test = 'range'; + if ($v >> 5 == 6) { // &110xxxxx 10xxxxx + $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left + $v = ($v - 192) << 6; + } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx + $next_byte = 1; + $v = ($v - 224) << 12; + } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 2; + $v = ($v - 240) << 18; + } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 3; + $v = ($v - 248) << 24; + } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + $next_byte = 4; + $v = ($v - 252) << 30; + } else { + throw new Exception('This might be UTF-8, but I don\'t understand it at byte '.$k); + return false; + } + if ('add' == $mode) { + $output[$out_len] = (int) $v; + ++$out_len; + continue; + } + } + if ('add' == $mode) { + if (!$this->_allow_overlong && $test == 'range') { + $test = 'none'; + if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { + throw new Exception('Bogus UTF-8 character detected (out of legal range) at byte '.$k); + return false; + } + } + if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx + $v = ($v - 128) << ($next_byte * 6); + $output[($out_len - 1)] += $v; + --$next_byte; + } else { + throw new Exception('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); + return false; + } + if ($next_byte < 0) { + $mode = 'next'; + } + } + } // for + return $output; + } + + /** + * Convert UCS-4 array into UTF-8 string. + * + * @throws Exception + * @access private + */ + private function _ucs4_to_utf8($input) + { + $output = ''; + + foreach ($input as $v) { + // $v = ord($v); + + if ($v < 128) { + // 7bit are transferred literally + $output .= chr($v); + } else if ($v < 1 << 11) { + // 2 bytes + $output .= chr(192 + ($v >> 6)) + . chr(128 + ($v & 63)); + } else if ($v < 1 << 16) { + // 3 bytes + $output .= chr(224 + ($v >> 12)) + . chr(128 + (($v >> 6) & 63)) + . chr(128 + ($v & 63)); + } else if ($v < 1 << 21) { + // 4 bytes + $output .= chr(240 + ($v >> 18)) + . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) + . chr(128 + ($v & 63)); + } else if ($v < 1 << 26) { + // 5 bytes + $output .= chr(248 + ($v >> 24)) + . chr(128 + (($v >> 18) & 63)) + . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) + . chr(128 + ($v & 63)); + } else if ($v < 1 << 31) { + // 6 bytes + $output .= chr(252 + ($v >> 30)) + . chr(128 + (($v >> 24) & 63)) + . chr(128 + (($v >> 18) & 63)) + . chr(128 + (($v >> 12) & 63)) + . chr(128 + (($v >> 6) & 63)) + . chr(128 + ($v & 63)); + } else { + throw new Exception('Conversion from UCS-4 to UTF-8 failed: malformed input at byte ' . $k); + } + } + + return $output; + } + + /** + * Convert UCS-4 array into UCS-4 string + * + * @throws Exception + * @access private + */ + private function _ucs4_to_ucs4_string($input) + { + $output = ''; + // Take array values and split output to 4 bytes per value + // The bit mask is 255, which reads &11111111 + foreach ($input as $v) { + $output .= ($v & (255 << 24) >> 24) . ($v & (255 << 16) >> 16) . ($v & (255 << 8) >> 8) . ($v & 255); + } + return $output; + } + + /** + * Convert UCS-4 strin into UCS-4 garray + * + * @throws Exception + * @access private + */ + private function _ucs4_string_to_ucs4($input) + { + $output = array(); + + $inp_len = strlen($input); + // Input length must be dividable by 4 + if ($inp_len % 4) { + throw new Exception('Input UCS4 string is broken'); + return false; + } + + // Empty input - return empty output + if (!$inp_len) return $output; + + for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { + // Increment output position every 4 input bytes + if (!$i % 4) { + $out_len++; + $output[$out_len] = 0; + } + $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); + } + return $output; + } + + /** + * Echo hex representation of UCS4 sequence. + * + * @param array $input UCS4 sequence + * @param boolean $include_bit Include bitmask in output + * @return void + * @static + * @access private + */ + private static function _showHex($input, $include_bit = false) + { + foreach ($input as $k => $v) { + echo '[', $k, '] => ', sprintf('%X', $v); + + if ($include_bit) { + echo ' (', Net_IDNA::_showBitmask($v), ')'; + } + + echo "\n"; + } + } + + /** + * Gives you a bit representation of given Byte (8 bits), Word (16 bits) or DWord (32 bits) + * Output width is automagically determined + * + * @static + * @access private + */ + private static function _showBitmask($octet) + { + if ($octet >= (1 << 16)) { + $w = 31; + } else if ($octet >= (1 << 8)) { + $w = 15; + } else { + $w = 7; + } + + $return = ''; + + for ($i = $w; $i > -1; $i--) { + $return .= ($octet & (1 << $i))? 1 : '0'; + } + + return $return; + } + // }}}} +} + +?> diff --git a/thirdparty/pear/Net/IMAP.php b/thirdparty/pear/Net/IMAP.php old mode 100644 new mode 100755 index bb8d733..bb8d733 --- a/thirdparty/pear/Net/IMAP.php +++ b/thirdparty/pear/Net/IMAP.php diff --git a/thirdparty/pear/Net/IMAPProtocol.php b/thirdparty/pear/Net/IMAPProtocol.php old mode 100644 new mode 100755 index 5c555f6..5c555f6 --- a/thirdparty/pear/Net/IMAPProtocol.php +++ b/thirdparty/pear/Net/IMAPProtocol.php diff --git a/thirdparty/pear/Net/IPv4.php b/thirdparty/pear/Net/IPv4.php old mode 100644 new mode 100755 index ba62a8d..ba62a8d --- a/thirdparty/pear/Net/IPv4.php +++ b/thirdparty/pear/Net/IPv4.php diff --git a/thirdparty/pear/Net/IPv6.php b/thirdparty/pear/Net/IPv6.php old mode 100644 new mode 100755 index c624028..c624028 --- a/thirdparty/pear/Net/IPv6.php +++ b/thirdparty/pear/Net/IPv6.php diff --git a/thirdparty/pear/Net/Ident.php b/thirdparty/pear/Net/Ident.php old mode 100644 new mode 100755 index c5dc283..c5dc283 --- a/thirdparty/pear/Net/Ident.php +++ b/thirdparty/pear/Net/Ident.php diff --git a/thirdparty/pear/Net/LDAP.php b/thirdparty/pear/Net/LDAP.php old mode 100644 new mode 100755 index 9c9e8ec..b562628 --- a/thirdparty/pear/Net/LDAP.php +++ b/thirdparty/pear/Net/LDAP.php @@ -1,583 +1,754 @@ +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: LDAP.php,v 1.95 2009/07/03 09:32:25 beni Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ /** - * Error constants for errors that are not LDAP errors - */ +* Package includes. +*/ +require_once 'PEAR.php'; +require_once 'LDAP/RootDSE.php'; +require_once 'Net/LDAP/Schema.php'; +require_once 'LDAP/Entry.php'; +require_once 'LDAP/Search.php'; +require_once 'LDAP/Util.php'; +require_once 'LDAP/Filter.php'; +require_once 'LDAP/LDIF.php'; -define ('NET_LDAP_ERROR', 1000); +/** +* Error constants for errors that are not LDAP errors. +*/ +define('NET_LDAP_ERROR', 1000); +/** +* Net_LDAP Version +*/ +define('NET_LDAP_VERSION', '1.1.5'); /** - * Net_LDAP - manipulate LDAP servers the right way! - * - * @author Tarjei Huse - * @author Jan Wagner - * @version $Revision$ - * @package Net_LDAP - */ - class Net_LDAP extends PEAR +* Net_LDAP - manipulate LDAP servers the right way! +* +* @category Net +* @package Net_LDAP +* @author Tarjej Huse +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ +class Net_LDAP extends PEAR { /** - * Class configuration array - * - * dn = the DN to bind as. - * host = the ldap host to connect to - * password = no explanation needed - * base = ldap base - * port = the server port - * tls = when set, ldap_start_tls() is run after connecting. - * version = ldap version (defaults to v 3) - * filter = default search filter - * scope = default search scope - * - * @access private - * @var array - */ - var $_config = array('dn' => '', - 'host' => 'localhost', - 'password' => '', - 'tls' => false, - 'base' => '', - 'port' => 389, - 'version' => 3, - 'options' => array(), - 'filter' => '(objectClass=*)', - 'scope' => 'sub'); + * Class configuration array + * + * host = the ldap host to connect to (may be an array of several hosts + * to try) + * port = the server port + * version = ldap version (defaults to v 3) + * starttls = when set, ldap_start_tls() is run after connecting. + * bindpw = no explanation needed + * binddn = the DN to bind as. + * basedn = ldap base + * options = hash of ldap options to set (opt => val) + * filter = default search filter + * scope = default search scope + * + * @access private + * @var array + */ + var $_config = array('host' => 'localhost', + 'port' => 389, + 'version' => 3, + 'starttls' => false, + 'binddn' => '', + 'bindpw' => '', + 'basedn' => '', + 'options' => array(), + 'filter' => '(objectClass=*)', + 'scope' => 'sub'); /** - * LDAP resource link. - * - * @access private - * @var resource - */ - var $_link; + * List of hosts we try to establish a connection to + * + * @access private + * @var array + */ + var $_host_list = array(); /** - * Net_LDAP Release Version - * - * @access private - * @var string - */ - var $_version = "0.6.6"; + * List of hosts that are known to be down. + * + * @access private + * @var array + */ + var $_down_host_list = array(); /** - * Net_LDAP_Schema object - * - * @access private - * @var object Net_LDAP_Schema - */ + * LDAP resource link. + * + * @access private + * @var resource + */ + var $_link = false; + + /** + * Net_LDAP_Schema object + * + * This gets set and returned by {@link schema()} + * + * @access private + * @var object Net_LDAP_Schema + */ var $_schema = null; /** - * Cache for attribute encoding checks - * - * @access private - * @var array Hash with attribute names as key and boolean value - * to determine whether they should be utf8 encoded or not. - */ + * Cache for attribute encoding checks + * + * @access private + * @var array Hash with attribute names as key and boolean value + * to determine whether they should be utf8 encoded or not. + */ var $_schemaAttrs = array(); /** - * Net_LDAP constructor - * - * Sets the config array - * - * @access protected - * @param array Configuration array - * @return void - * @see $_config - */ - function Net_LDAP($_config = array()) + * Returns the Net_LDAP Release version, may be called statically + * + * @static + * @return string Net_LDAP version + */ + function getVersion() { - $this->PEAR('Net_LDAP_Error'); - - foreach ($_config as $k => $v) { - $this->_config[$k] = $v; - } + return NET_LDAP_VERSION; } /** - * Creates the initial ldap-object - * - * Static function that returns either an error object or the new Net_LDAP object. - * Something like a factory. Takes a config array with the needed parameters. - * - * @access public - * @param array Configuration array - * @return mixed object Net_LDAP_Error or Net_LDAP - * @see $_config - */ + * Creates the initial ldap-object + * + * Static function that returns either an error object or the new Net_LDAP + * object. Something like a factory. Takes a config array with the needed + * parameters. + * + * @param array $config Configuration array + * + * @access public + * @return Net_LDAP_Error|Net_LDAP Net_LDAP_Error or Net_LDAP object + */ function &connect($config = array()) { - if (!function_exists('ldap_connect')){ - return Net_LDAP::raiseError("It seems that you do not have the ldap-extension installed. Please install it before using this package."); + $ldap_check = Net_LDAP::checkLDAPExtension(); + if (Net_LDAP::iserror($ldap_check)) { + return $ldap_check; } - @$obj =& new Net_LDAP($config); - $err = $obj->bind(); + @$obj = & new Net_LDAP($config); + + // todo? better errorhandling for setConfig()? + + // connect and bind with credentials in config + $err = $obj->bind(); if (Net_LDAP::isError($err)) { return $err; } + return $obj; } /** - * Bind to the ldap-server - * - * The function may be used if you do not create the object using Net_LDAP::connect. - * - * @access public - * @param array Configuration array - * @return mixed Net_LDAP_Error or true - * @see $_config - */ - function bind($config = array()) + * Net_LDAP constructor + * + * Sets the config array + * + * Please note that the usual way of getting Net_LDAP to work is + * to call something like: + * $ldap = Net_LDAP::connect($ldap_config); + * + * @param array $config Configuration array + * + * @access protected + * @return void + * @see $_config + */ + function Net_LDAP($config = array()) + { + $this->PEAR('Net_LDAP_Error'); + $this->_setConfig($config); + } + + /** + * Sets the internal configuration array + * + * @param array $config Configuration array + * + * @access private + * @return void + */ + function _setConfig($config) { + // + // Parameter check -- probably should raise an error here if config + // is not an array. + // + if (! is_array($config)) { + return; + } + foreach ($config as $k => $v) { - $this->_config[$k] = $v; + if (isset($this->_config[$k])) { + $this->_config[$k] = $v; + } else { + // map old (Net_LDAP) parms to new ones + switch($k) { + case "dn": + $this->_config["binddn"] = $v; + break; + case "password": + $this->_config["bindpw"] = $v; + break; + case "tls": + $this->_config["starttls"] = $v; + break; + case "base": + $this->_config["basedn"] = $v; + break; + } + } } - if ($this->_config['host']) { - $this->_link = @ldap_connect($this->_config['host'], $this->_config['port']); + // + // Ensure the host list is an array. + // + if (is_array($this->_config['host'])) { + $this->_host_list = $this->_config['host']; } else { - return $this->raiseError("Host not defined in config. {$this->_config['host']}"); + if (strlen($this->_config['host']) > 0) { + $this->_host_list = array($this->_config['host']); + } else { + // this will cause an error in _connect(), so the user is notified + $this->_host_list = array(); + } } - if (!$this->_link) { - // there is no good errorcode for this one! I chose 52. - return $this->raiseError("Could not connect to server. ldap_connect failed.", 52); - } - // You must set the version and start tls BEFORE binding! + // + // Reset the down host list, which seems like a sensible thing to do + // if the config is being reset for some reason. + // + $this->_down_host_list = array(); + } - if ($this->_config['version'] != 2 && Net_LDAP::isError($msg = $this->setLDAPVersion())) { - return $msg; + /** + * Bind or rebind to the ldap-server + * + * This function binds with the given dn and password to the server. In case + * no connection has been made yet, it will be startet and startTLS issued + * if appropiate. + * + * The internal bind configuration is not being updated, so if you call + * bind() without parameters, you can rebind with the credentials + * provided at first connecting to the server. + * + * @param string $dn Distinguished name for binding + * @param string $password Password for binding + * + * @access public + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ + function bind($dn = null, $password = null) + { + // fetch current bind credentials + if (is_null($dn)) { + $dn = $this->_config["binddn"]; } - - if ($this->_config['tls'] && Net_LDAP::isError($msg = $this->startTLS())) { - return $msg; + if (is_null($password)) { + $password = $this->_config["bindpw"]; } - if (isset($this->_config['options']) && - is_array($this->_config['options']) && - count($this->_config['options'])) - { - foreach ($this->_config['options'] as $opt => $val) { - $err = $this->setOption($opt, $val); - if (Net_LDAP::isError($err)) { - return $err; - } + // Connect first, if we haven't so far. + // This will also bind us to the server. + if ($this->_link === false) { + // store old credentials so we can revert them later + // then overwrite config with new bind credentials + $olddn = $this->_config["binddn"]; + $oldpw = $this->_config["bindpw"]; + + // overwrite bind credentials in config + // so _connect() knows about them + $this->_config["binddn"] = $dn; + $this->_config["bindpw"] = $password; + + // try to connect with provided credentials + $msg = $this->_connect(); + + // reset to previous config + $this->_config["binddn"] = $olddn; + $this->_config["bindpw"] = $oldpw; + + // see if bind worked + if (Net_LDAP::isError($msg)) { + return $msg; } - } - - if (isset($this->_config['dn']) && isset($this->_config['password'])) { - $bind = @ldap_bind($this->_link, $this->_config['dn'], $this->_config['password']); } else { - $bind = @ldap_bind($this->_link); - } - - if (!$bind) { - return $this->raiseError("Bind failed " . @ldap_error($this->_link), @ldap_errno($this->_link)); + // do the requested bind as we are + // asked to bind manually + if (is_null($dn)) { + $msg = @ldap_bind($this->_link); // anonymous bind + } else { + $msg = @ldap_bind($this->_link, $dn, $password); // privilegued bind + } + if (false === $msg) { + return PEAR::raiseError("Bind failed: " . + @ldap_error($this->_link), + @ldap_errno($this->_link)); + } } - return true; } /** - * ReBind to the ldap-server using another dn and password - * - * The function may be used if you do not create the object using Net_LDAP::connect. - * - * @access public - * @param string $dn - the DN to bind as. - * string $password - the bassword to use. - * @return mixed Net_LDAP_Error or true - * @see $_config - */ - - function reBind ($dn = null, $password = null) + * Connect to the ldap-server + * + * This function connects to the given LDAP server. + * + * @access private + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ + function _connect() { - if ($dn && $password ) { - $bind = @ldap_bind($this->_link, $dn, $password); - } else { - $bind = @ldap_bind($this->_link); + // + // Return true if we are already connected. + // + if ($this->_link !== false) { + return true; } - if (!$bind) { - return $this->raiseError("Bind failed " . @ldap_error($this->_link), @ldap_errno($this->_link)); + // + // Connnect to the LDAP server if we are not connected. Note that + // with some LDAP clients, ldap_connect returns a link value even + // if no connection is made. We need to do at least one anonymous + // bind to ensure that a connection is actually valid. + // + // Ref: http://www.php.net/manual/en/function.ldap-connect.php + // + + // + // Default error message in case all connection attempts fail but no message is set + // + $current_error = new PEAR_Error('Unknown connection error'); + + // + // Catch empty $_host_list arrays. + // + if (!is_array($this->_host_list) || count($this->_host_list) == 0) { + $current_error = PEAR::raiseError('No Servers configured! Please pass in an array of servers to Net_LDAP2'); + return $current_error; } - return true; + + // + // Cycle through the host list. + // + foreach ($this->_host_list as $host) { + + // + // Ensure we have a valid string for host name + // + if (is_array($host)) { + $current_error = PEAR::raiseError('No Servers configured! Please pass in an one dimensional array of servers to Net_LDAP2! (multidimensional array detected!)'); + continue; + } + + // + // Skip this host if it is known to be down. + // + if (in_array($host, $this->_down_host_list)) { + continue; + } + + // + // Record the host that we are actually connecting to in case + // we need it later. + // + $this->_config['host'] = $host; + + // + // Attempt a connection. + // + $this->_link = @ldap_connect($host, $this->_config['port']); + if (false === $this->_link) { + $current_error = PEAR::raiseError('Could not connect to ' . + $host . ':' . $this->_config['port']); + $this->_down_host_list[] = $host; + continue; + } + + // + // If we're supposed to use TLS, do so before we try to bind. + // + if ($this->_config["starttls"] === true) { + if (self::isError($msg = $this->startTLS())) { + $current_error = $msg; + $this->_link = false; + $this->_down_host_list[] = $host; + continue; + } + } + + // + // Attempt to bind to the server. If we have credentials configured, + // we try to use them, otherwise its an anonymous bind. + // + $msg = $this->bind(); + //var_dump($msg); exit; + if (self::isError($msg)) { + // The bind failed, discard link and save error msg. + // Then record the host as down and try next one + $this->_link = false; + $current_error = $msg; + $this->_down_host_list[] = $host; + continue; + } + + // + // Set LDAP version after we have a bind. + // + if (self::isError($msg = $this->setLDAPVersion())) { + $current_error = $msg; + $this->_link = false; + $this->_down_host_list[] = $host; + continue; + } + + // + // Set LDAP parameters, now we know we have a valid connection. + // + if (isset($this->_config['options']) && + is_array($this->_config['options']) && + count($this->_config['options'])) { + foreach ($this->_config['options'] as $opt => $val) { + $err = $this->setOption($opt, $val); + if (self::isError($err)) { + $current_error = $err; + $this->_link = false; + $this->_down_host_list[] = $host; + continue 2; + } + } + } + + // + // At this stage we have connected, bound, and set up options, + // so we have a known good LDAP server. Time to go home. + // + return true; + } + + + // + // All connection attempts have failed, return the last error. + // + return $current_error; } /** - * Starts an encrypted session - * - * @access public - * @return mixed True or Net_LDAP_Error - */ + * Starts an encrypted session + * + * @access public + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ function startTLS() { - if (!@ldap_start_tls($this->_link)) { - return $this->raiseError("TLS not started. Error:" . @ldap_error($this->_link), @ldap_errno($this->_link)); + if (false === @ldap_start_tls($this->_link)) { + return $this->raiseError("TLS not started: " . + @ldap_error($this->_link), + @ldap_errno($this->_link)); } return true; } /** - * alias function of startTLS() for perl-ldap interface - * - * @see startTLS() - */ + * alias function of startTLS() for perl-ldap interface + * + * @return void + * @see startTLS() + */ function start_tls() { $args = func_get_args(); - return call_user_func_array(array($this, 'startTLS' ), $args); + return call_user_func_array(array( &$this, 'startTLS' ), $args); } /** - * Close LDAP connection. - * - * Closes the connection. Use this when the session is over. - * - * @return void - */ + * Close LDAP connection. + * + * Closes the connection. Use this when the session is over. + * + * @return void + */ function done() { $this->_Net_LDAP(); } /** - * Destructor - * - * @access private - */ + * Destructor + * + * @access private + */ function _Net_LDAP() { @ldap_close($this->_link); } /** - * Add a new entryobject to a directory. - * - * Use add to add a new Net_LDAP_Entry object to the directory. - * - * @param object Net_LDAP_Entry - * @return mixed Net_LDAP_Error or true - */ - function add($entry) + * Add a new entryobject to a directory. + * + * Use add to add a new Net_LDAP_Entry object to the directory. + * This also links the entry to the connection used for the add, + * if it was a fresh entry ({@link Net_LDAP_Entry::createFresh()}) + * + * @param Net_LDAP_Entry &$entry Net_LDAP_Entry + * + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ + function add(&$entry) { - if (@ldap_add($this->_link, $entry->dn(), $entry->attributes())) { - return true; + if (false === is_a($entry, 'Net_LDAP_Entry')) { + return PEAR::raiseError('Parameter to Net_LDAP::add() must be a Net_LDAP_Entry object.'); + } + if (@ldap_add($this->_link, $entry->dn(), $entry->getValues())) { + // entry successfully added, we should update its $ldap reference + // in case it is not set so far (fresh entry) + if (!is_a($entry->getLDAP(), 'Net_LDAP')) { + $entry->setLDAP($this); + } + // store, that the entry is present inside the directory + $entry->_markAsNew(false); + return true; } else { - return $this->raiseError("Could not add entry " . $entry->dn() . " " . @ldap_error($this->_link), - @ldap_errno($this->_link)); + return PEAR::raiseError("Could not add entry " . $entry->dn() . " " . + @ldap_error($this->_link), + @ldap_errno($this->_link)); } } /** - * Delete an entry from the directory - * - * The object may either be a string representing the dn or a Net_LDAP_Entry object. - * The param array may contain a boolean value named recursive. When set, all subentries - * of the Entry will be deleted as well - * - * @access public - * @param mixed string or Net_LDAP_Entry - * @param array - * @return mixed Net_LDAP_Error or true - */ - function delete($dn, $param = array()) + * Delete an entry from the directory + * + * The object may either be a string representing the dn or a Net_LDAP_Entry + * object. When the boolean paramter recursive is set, all subentries of the + * entry will be deleted as well. + * + * @param string|Net_LDAP_Entry $dn DN-string or Net_LDAP_Entry + * @param boolean $recursive Should we delete all children recursive as well? + * + * @access public + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ + function delete($dn, $recursive = false) { - if (is_object($dn) && strtolower(get_class($dn)) == 'net_ldap_entry') { + if (is_a($dn, 'Net_LDAP_Entry')) { $dn = $dn->dn(); - } else { - if (!is_string($dn)) { - // this is what the server would say: invalid_dn_syntax. - return $this->raiseError("$dn not a string nor an entryobject!", 34); - } } - - if ($param['recursive'] ) { - $searchresult = @ldap_list($this->_link, $dn, '(objectClass=*)', array()); - - if ($searchresult) { - $entries = @ldap_get_entries($this->_link, $searchresult); - - for ($i=0; $i<$entries['count']; $i++) { - $result = $this->delete($entries[$i]['dn'], array('recursive' => true)); - if (!$result) { - $errno = @ldap_errno($this->_link); - return $this->raiseMessage ("Net_LDAP::delete: " . $this->errorMessage($errno), $errno); - } - if(PEAR::isError($result)){ - return $result; - } + if (false === is_string($dn)) { + return PEAR::raiseError("Parameter is not a string nor an entry object!"); + } + // Recursive delete searches for children and calls delete for them + if ($recursive) { + $result = @ldap_list($this->_link, $dn, '(objectClass=*)', array(null), 0, 0); + if (@ldap_count_entries($this->_link, $result)) { + $subentry = @ldap_first_entry($this->_link, $result); + $this->delete(@ldap_get_dn($this->_link, $subentry), true); + while ($subentry = @ldap_next_entry($this->_link, $subentry)) { + $this->delete(@ldap_get_dn($this->_link, $subentry), true); } } } - if (!@ldap_delete($this->_link, $dn)) { - $error = ldap_errno($this->_link ); + // Delete the DN + if (false == @ldap_delete($this->_link, $dn)) { + $error = @ldap_errno($this->_link); if ($error == 66) { - /* entry has subentries */ - return $this->raiseError('Net_LDAP::delete: Cound not delete entry ' . $dn . - ' because of subentries. Use the recursive param to delete them.'); + return PEAR::raiseError("Could not delete entry $dn because of subentries. Use the recursive param to delete them."); } else { - return $this->raiseError("Net_LDAP::delete: Could not delete entry " . $dn ." because: ". - $this->errorMessage($error), $error); + return PEAR::raiseError("Could not delete entry $dn: " . + $this->errorMessage($error), $error); } } return true; } /** - * Modify an ldapentry - * - * This is taken from the perlpod of net::ldap, and explains things quite nicely. - * modify ( DN, OPTIONS ) - * Modify the contents of DN on the server. DN May be a - * string or a Net::LDAP::Entry object. - * - * dn This option is here for compatibility only, and - * may be removed in future. Previous releases did - * not take the DN argument which replaces this - * option. - * - * add The add option should be a reference to a HASH. - * The values of the HASH are the attributes to add, - * and the values may be a string or a reference to a - * list of values. - * - * delete - * A reference to an ARRAY of attributes to delete. - * TODO: This does not support deleting one or two values yet - use - * replace. - * - * replace - * The option takes a argument in the same - * form as add, but will cause any existing - * attributes with the same name to be replaced. If - * the value for any attribute in the rray is a ref - * erence to an empty string the all instances of the - * attribute will be deleted. - * - * changes - * This is an alternative to add, delete and replace - * where the whole operation can be given in a single - * argument. The argument should be a array - * - * Values in the ARRAY are used in pairs, the first - * is the operation add, delete or replace and the - * second is a reference to an ARRAY of attribute - * values. - * - * The attribute value list is also used in pairs. - * The first value in each pair is the attribute name - * and the second is a reference to a list of values. - * - * Example: - * $ldap->modify ( $dn, array (changes => array( - * 'delete' => array('faxNumber' => ''), - * 'add' => array('sn' => 'Barr'), - * 'replace' => array(email => 'tarjei@nu.no')))); - * - * @access public - * @param string - * @param array - * @return mixed Net_LDAP_Error or true - */ - function modify($dn , $params = array()) + * Modify an ldapentry directly on the server + * + * This one takes the DN or a Net_LDAP_Entry object and an array of actions. + * This array should be something like this: + * + * array('add' => array('attribute1' => array('val1', 'val2'), + * 'attribute2' => array('val1')), + * 'delete' => array('attribute1'), + * 'replace' => array('attribute1' => array('val1')), + * 'changes' => array('add' => ..., + * 'replace' => ..., + * 'delete' => array('attribute1', 'attribute2' => array('val1'))) + * + * The changes array is there so the order of operations can be influenced + * (the operations are done in order of appearance). + * The order of execution is as following: + * 1. adds from 'add' array + * 2. deletes from 'delete' array + * 3. replaces from 'replace' array + * 4. changes (add, replace, delete) in order of appearance + * All subarrays (add, replace, delete, changes) may be given at the same time. + * + * The function calls the corresponding functions of an Net_LDAP_Entry + * object. A detailed description of array structures can be found there. + * + * Unlike the modification methods provided by the Net_LDAP_Entry object, + * this method will instantly carry out an update() after each operation, + * thus modifying "directly" on the server. + * + * @param string|Net_LDAP_Entry &$entry DN-string or Net_LDAP_Entry + * @param array $parms Array of changes + * + * @access public + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ + function modify(&$entry , $parms = array()) { - if (is_object($dn)) { - $dn = $dn->dn(); - } - // since $params['dn'] is not used in net::ldap now: - if (isset($params['dn'])) { - return $this->raiseError("This feature will not be implemented!"); + if (is_string($entry)) { + $entry = $this->getEntry($entry); + if (Net_LDAP::isError($entry)) { + return $entry; + } } - // new code from rafael at krysciak dot de - if(array_key_exists('changes', $params)) { - $_params = $params; - } else { - $_params['changes'] = $params; + if (!is_a($entry, 'Net_LDAP_Entry')) { + return PEAR::raiseError("Parameter is not a string nor an entry object!"); } - if (is_array($_params['changes'])) { - foreach($_params['changes'] AS $option => $atrr) { - switch($option) { - case 'add': - $command = $dn_exists ? 'ldap_mod_add':'ldap_add'; - break; - case 'replace': - $command = 'ldap_mod_replace'; - break; - case 'delete': - $command = 'ldap_mod_del'; - // to delete an attribute with a specific value you - // need a hash array('attr_name' => array('attr_value_1', ... ,'attr_value_n')) - // the hash array('attr_name' => 'attr_value') will be converted - // automatically to array('attr_name' => array('attr_value')) - foreach($atrr AS $atrr_field => $atrr_value) { - if(!is_array($atrr_value)) { - $atrr[$atrr_field] = array($atrr_value); - } - } - break; - default: - return $this->raiseError("Net_LDAP::modify: not supported option " . $option); - break; - } // end switch($option) { - if(!@call_user_func($command, $this->_link, $dn, $atrr)) { - return $this->raiseError("Net_LDAP::modify: $dn not modified because:" . ldap_error($this->_link), ldap_errno($this->_link)); + foreach (array('add', 'delete', 'replace') as $action) { + if (isset($parms[$action])) { + $msg = $entry->$action($parms[$action]); + if (Net_LDAP::isError($msg)) { + return $msg; + } + $entry->setLDAP($this); + $msg = $entry->update(); + if (Net_LDAP::isError($msg)) { + return PEAR::raiseError("Could not modify entry: ".$msg->getMessage()); } - } // end foreach($_params['changes'] AS $option => $atrr) { - } // end if (is_array($_params['changes'])) { - // everything went fine :) - return true; - - /* old broken code see bug#2987 - if (isset($params['changes'])) { - - if (isset($params['changes']['add']) && - !@ldap_modify($this->_link, $dn, $params['changes']['add'])) { - - return $this->raiseError("Net_LDAP::modify: $dn not modified because:" . ldap_error($this->_link), - ldap_errno($this->_link)); - } - - if (isset($params['changes']['replace']) && - !@ldap_modify($this->_link, $dn, $params['changes']['replace'])) { - - return $this->raiseError("Net_LDAP::modify: replace change didn't work: " . ldap_error($this->_link), - ldap_errno($this->_link)); - } - - if (isset($params['changes']['delete']) && - !@ldap_mod_del($this->_link, $dn, $params['changes']['delete'])) { - - return $this->raiseError("Net_LDAP::modify:delete did not work" . ldap_error($this->_link), - ldap_errno($this->_link)); - } - } - - if (isset($params['add']) && !@ldap_add($this->_link, $dn, $params['add'])) { - return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link)); - } - - if (isset($params['replace']) && !@ldap_modify($this->_link, $dn, $params['replace'])) { - return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link)); - } - - if (isset($params['delete'])) { - // since you delete an attribute by making it empty: - foreach ($params['delete'] as $k) { - $params['delete'][$k] = ''; } + } - if (!@ldap_modify($this->_link, $dn, $params['delete'])) { - return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link)); + if (isset($parms['changes']) && is_array($parms['changes'])) { + foreach ($parms['changes'] as $action => $value) { + $msg = $this->modify($entry, array($action => $value)); + if (Net_LDAP::isError($msg)) { + return $msg; + } } } - // everything went fine :) - return true; - */ + return true; } /** - * Run a ldap query - * - * Search is used to query the ldap-database. - * $base and $filter may be ommitted. BaseDN and default filter will then be used. - * Params may contain: - * - * scope: The scope which will be used for searching - * base - Just one entry - * sub - The whole tree - * one - Immediately below $base - * sizelimit: Limit the number of entries returned (default: 0), - * timelimit: Limit the time spent for searching (default: 0), - * attrsonly: If true, the search will only return the attribute names, NO values - * attributes: Array of attribute names, which the entry should contain. It is good practice - * to limit this to just the ones you need, so by default this function does not - * return any attributes at all. - * [NOT IMPLEMENTED] - * deref: By default aliases are dereferenced to locate the base object for the search, but not when - * searching subordinates of the base object. This may be changed by specifying one of the - * following values: - * - * never - Do not dereference aliases in searching or in locating the base object of the search. - * search - Dereference aliases in subordinates of the base object in searching, but not in - * locating the base object of the search. - * find - * always - * - * @access public - * @param string LDAP searchbase - * @param string LDAP search filter - * @param array Array of options - * @return object mixed Net_LDAP_Search or Net_LDAP_Error - */ + * Run a ldap query + * + * Search is used to query the ldap-database. + * $base and $filter may be ommitted.The one from config will then be used. + * Params may contain: + * + * scope: The scope which will be used for searching + * base - Just one entry + * sub - The whole tree + * one - Immediately below $base + * sizelimit: Limit the number of entries returned (default: 0 = unlimited), + * timelimit: Limit the time spent for searching (default: 0 = unlimited), + * attrsonly: If true, the search will only return the attribute names, + * attributes: Array of attribute names, which the entry should contain. + * It is good practice to limit this to just the ones you need. + * [NOT IMPLEMENTED] + * deref: By default aliases are dereferenced to locate the base object for the search, but not when + * searching subordinates of the base object. This may be changed by specifying one of the + * following values: + * + * never - Do not dereference aliases in searching or in locating the base object of the search. + * search - Dereference aliases in subordinates of the base object in searching, but not in + * locating the base object of the search. + * find + * always + * + * Please note, that you cannot override server side limitations to sizelimit + * and timelimit: You can always only lower a given limit. + * + * @param string $base LDAP searchbase + * @param string|Net_LDAP_Filter $filter LDAP search filter or a Net_LDAP_Filter object + * @param array $params Array of options + * + * @access public + * @return Net_LDAP_Search|Net_LDAP_Error Net_LDAP_Search object or Net_LDAP_Error object + */ function search($base = null, $filter = null, $params = array()) { - if (is_null($base)) { - $base = $this->_config['base']; + if (is_null($base)) { + $base = $this->_config['basedn']; } + if (is_null($filter)) { $filter = $this->_config['filter']; } + if (is_a($filter, 'Net_LDAP_Filter')) { + $filter = $filter->asString(); // convert Net_LDAP_Filter to string representation + } + + if (PEAR::isError($filter)) { + return $filter; + } + /* setting searchparameters */ - (isset($params['sizelimit'])) ? $sizelimit = $params['sizelimit'] : $sizelimit = 0; - (isset($params['timelimit'])) ? $timelimit = $params['timelimit'] : $timelimit = 0; - (isset($params['attrsonly'])) ? $attrsonly = $params['attrsonly'] : $attrsonly = 0; - (isset($params['attributes'])) ? $attributes = $params['attributes'] : $attributes = array(''); + $sizelimit = isset($params['sizelimit']) ? $params['sizelimit'] : 0; + $timelimit = isset($params['timelimit']) ? $params['timelimit'] : 0; + $attrsonly = isset($params['attrsonly']) ? $params['attrsonly'] : 0; + + $attributes = isset($params['attributes'])? $params['attributes'] : array(); + // Ensure $attributes to be an array in case only one + // attribute name was given as string if (!is_array($attributes)) { - $this->raiseError("The param attributes must be an array!"); + $attributes = array($attributes); } - /* scoping makes searches faster! */ + // reorganize the $attributes array index keys + // sometimes there are problems with not consecutive indexes + $attributes = array_values($attributes); + + // scoping makes searches faster! $scope = (isset($params['scope']) ? $params['scope'] : $this->_config['scope']); switch ($scope) { - case 'one': - $search_function = 'ldap_list'; - break; - case 'base': - $search_function = 'ldap_read'; - break; - default: - $search_function = 'ldap_search'; + case 'one': + $search_function = 'ldap_list'; + break; + case 'base': + $search_function = 'ldap_read'; + break; + default: + $search_function = 'ldap_search'; } $search = @call_user_func($search_function, @@ -589,16 +760,13 @@ define ('NET_LDAP_ERROR', 1000); $sizelimit, $timelimit); - if ($err = ldap_errno($this->_link)) { - + if ($err = @ldap_errno($this->_link)) { if ($err == 32) { // Errorcode 32 = no such object, i.e. a nullresult. - return $obj =& new Net_LDAP_Search ($search, $this->_link); - - // Errorcode 4 = sizelimit exeeded. this will be handled better in time... - //} elseif ($err == 4) { - // return $obj = & new Net_LDAP_Search ($search, $this->_link); - + return $obj = & new Net_LDAP_Search ($search, $this, $attributes); + } elseif ($err == 4) { + // Errorcode 4 = sizelimit exeeded. + return $obj = & new Net_LDAP_Search ($search, $this, $attributes); } elseif ($err == 87) { // bad search filter return $this->raiseError($this->errorMessage($err) . "($filter)", $err); @@ -607,20 +775,19 @@ define ('NET_LDAP_ERROR', 1000); return $this->raiseError($this->errorMessage($err) . $msg, $err); } } else { - @$obj =& new Net_LDAP_Search($search, $this->_link); - return $obj; + return $obj = & new Net_LDAP_Search($search, $this, $attributes); } - } /** - * Set an LDAP option - * - * @access public - * @param string Option to set - * @param mixed Value to set Option to - * @return mixed Net_LDAP_Error or true - */ + * Set an LDAP option + * + * @param string $option Option to set + * @param mixed $value Value to set Option to + * + * @access public + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ function setOption($option, $value) { if ($this->_link) { @@ -641,17 +808,18 @@ define ('NET_LDAP_ERROR', 1000); return $this->raiseError("Unkown Option requested"); } } else { - return $this->raiseError("No LDAP connection"); + return $this->raiseError("Could not set LDAP option: No LDAP connection"); } } /** - * Get an LDAP option value - * - * @access public - * @param string Option to get - * @return mixed Net_LDAP_Error or option value - */ + * Get an LDAP option value + * + * @param string $option Option to get + * + * @access public + * @return Net_LDAP_Error|string Net_LDAP_Error or option value + */ function getOption($option) { if ($this->_link) { @@ -677,16 +845,16 @@ define ('NET_LDAP_ERROR', 1000); } /** - * Get the LDAP_PROTOCOL_VERSION that is used on the connection. - * - * A lot of ldap functionality is defined by what protocol version the ldap server speaks. - * This might be 2 or 3. - * - * @return int - */ + * Get the LDAP_PROTOCOL_VERSION that is used on the connection. + * + * A lot of ldap functionality is defined by what protocol version the ldap server speaks. + * This might be 2 or 3. + * + * @return int + */ function getLDAPVersion() { - if($this->_link) { + if ($this->_link) { $version = $this->getOption("LDAP_OPT_PROTOCOL_VERSION"); } else { $version = $this->_config['version']; @@ -695,11 +863,12 @@ define ('NET_LDAP_ERROR', 1000); } /** - * Set the LDAP_PROTOCOL_VERSION that is used on the connection. - * - * @param int Version to set - * @return mixed Net_LDAP_Error or TRUE - */ + * Set the LDAP_PROTOCOL_VERSION that is used on the connection. + * + * @param int $version LDAP-version that should be used + * + * @return Net_LDAP_Error|true Net_LDAP_Error object or true + */ function setLDAPVersion($version = 0) { if (!$version) { @@ -708,77 +877,181 @@ define ('NET_LDAP_ERROR', 1000); return $this->setOption("LDAP_OPT_PROTOCOL_VERSION", $version); } - /** - * Get the Net_LDAP version. - * - * Return the Net_LDAP version - * - * @return string Net_LDAP version - */ - function getVersion () - { - return $this->_version; - } /** - * Tell if a dn already exists - * - * @param string - * @return boolean - */ + * Tell if a DN does exist in the directory + * + * @param string $dn The DN of the object to test + * + * @return boolean|Net_LDAP_Error + */ function dnExists($dn) { - $dns = explode(",",$dn); - $filter = array_shift($dns); - $base= implode($dns,','); - //$base = $dn; - //$filter = '(objectclass=*)'; + if (!is_string($dn)) { + return PEAR::raiseError('$dn is expected to be a string but is '.gettype($dn).' '.get_class($dn)); + } - $result = @ldap_list($this->_link, $base, $filter, array(), 1, 1); - if (ldap_errno($this->_link) == 32) { - return false; + // make dn relative to parent + $base = Net_LDAP_Util::ldap_explode_dn($dn, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false)); + if (Net_LDAP::isError($base)) { + return $base; } - if (ldap_errno($this->_link) != 0) { - $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link)); + $entry_rdn = array_shift($base); + if (is_array($entry_rdn)) { + // maybe the dn consist of a multivalued RDN, we must build the dn in this case + // because the $entry_rdn is an array! + $filter_dn = Net_LDAP_Util::canonical_dn($entry_rdn); } + $base = Net_LDAP_Util::canonical_dn($base); + + $result = @ldap_list($this->_link, $base, $entry_rdn, array(), 1, 1); if (@ldap_count_entries($this->_link, $result)) { return true; } + if (ldap_errno($this->_link) == 32) { + return false; + } + if (ldap_errno($this->_link) != 0) { + return PEAR::raiseError(ldap_error($this->_link), ldap_errno($this->_link)); + } return false; } - /** - * Get a specific entry based on the dn + /** + * Get a specific entry based on the DN + * + * @param string $dn DN of the entry that should be fetched + * @param array $attr Array of Attributes to select * - * @param string dn - * @param array Array of Attributes to select - * @return object Net_LDAP_Entry or Net_LDAP_Error + * @return Net_LDAP_Entry|Net_LDAP_Error Reference to a Net_LDAP_Entry object or Net_LDAP_Error object + * @todo Maybe check against the shema should be done to be sure the attribute type exists */ - function &getEntry($dn, $attr = array('')) - { - $result = $this->search($dn, '(objectClass=*)', array('scope' => 'base', 'attributes' => $attr)); + function &getEntry($dn, $attr = array()) + { + if (!is_array($attr)) { + $attr = array($attr); + } + $result = $this->search($dn, '(objectClass=*)', + array('scope' => 'base', 'attributes' => $attr)); if (Net_LDAP::isError($result)) { return $result; + } elseif ($result->count() == 0) { + return PEAR::raiseError('Could not fetch entry '.$dn.': no entry found'); } $entry = $result->shiftEntry(); if (false == $entry) { - return $this->raiseError('Could not fetch entry'); + return PEAR::raiseError('Could not fetch entry (error retrieving entry from search result)'); } return $entry; - } + } + /** + * Rename or move an entry + * + * This method will instantly carry out an update() after the move, + * so the entry is moved instantly. + * You can pass an optional Net_LDAP object. In this case, a cross directory + * move will be performed which deletes the entry in the source (THIS) directory + * and adds it in the directory $target_ldap. + * A cross directory move will switch the Entrys internal LDAP reference so + * updates to the entry will go to the new directory. + * + * Note that if you want to do a cross directory move, you need to + * pass an Net_LDAP_Entry object, otherwise the attributes will be empty. + * + * @param string|Net_LDAP_Entry &$entry Entry DN or Entry object + * @param string $newdn New location + * @param Net_LDAP $target_ldap (optional) Target directory for cross server move; should be passed via reference + * + * @return Net_LDAP_Error|true + */ + function move(&$entry, $newdn, $target_ldap = null) + { + if (is_string($entry)) { + $entry_o = $this->getEntry($entry); + } else { + $entry_o =& $entry; + } + if (!is_a($entry_o, 'Net_LDAP_Entry')) { + return PEAR::raiseError('Parameter $entry is expected to be a Net_LDAP_Entry object! (If DN was passed, conversion failed)'); + } + if (null !== $target_ldap && !is_a($target_ldap, 'Net_LDAP')) { + return PEAR::raiseError('Parameter $target_ldap is expected to be a Net_LDAP object!'); + } + + if ($target_ldap && $target_ldap !== $this) { + // cross directory move + if (is_string($entry)) { + return PEAR::raiseError('Unable to perform cross directory move: operation requires a Net_LDAP_Entry object'); + } + if ($target_ldap->dnExists($newdn)) { + return PEAR::raiseError('Unable to perform cross directory move: entry does exist in target directory'); + } + $entry_o->dn($newdn); + $res = $target_ldap->add($entry_o); + if (Net_LDAP::isError($res)) { + return PEAR::raiseError('Unable to perform cross directory move: '.$res->getMessage().' in target directory'); + } + $res = $this->delete($entry_o->currentDN()); + if (Net_LDAP::isError($res)) { + $res2 = $target_ldap->delete($entry_o); // undo add + if (Net_LDAP::isError($res2)) { + $add_error_string = 'Additionally, the deletion (undo add) of $entry in target directory failed.'; + } + return PEAR::raiseError('Unable to perform cross directory move: '.$res->getMessage().' in source directory. '.$add_error_string); + } + $entry_o->setLDAP($target_ldap); + return true; + } else { + // local move + $entry_o->dn($newdn); + $entry_o->setLDAP($this); + return $entry_o->update(); + } + } /** - * Returns the string for an ldap errorcode. - * - * Made to be able to make better errorhandling - * Function based on DB::errorMessage() - * Tip: The best description of the errorcodes is found here: http://www.directory-info.com/LDAP/LDAPErrorCodes.html - * - * @param int Error code - * @return string The errorstring for the error. - */ + * Copy an entry to a new location + * + * The entry will be immediately copied. + * Please note that only attributes you have + * selected will be copied. + * + * @param Net_LDAP_Entry &$entry Entry object + * @param string $newdn New FQF-DN of the entry + * + * @return Net_LDAP_Error|Net_LDAP_Entry Error Message or reference to the copied entry + */ + function ©(&$entry, $newdn) + { + if (!is_a($entry, 'Net_LDAP_Entry')) { + return PEAR::raiseError('Parameter $entry is expected to be a Net_LDAP_Entry object!'); + } + + $newentry = Net_LDAP_Entry::createFresh($newdn, $entry->getValues()); + $result = $this->add($newentry); + + if (is_a($result, 'Net_LDAP_Error')) { + return $result; + } else { + return $newentry; + } + } + + + /** + * Returns the string for an ldap errorcode. + * + * Made to be able to make better errorhandling + * Function based on DB::errorMessage() + * Tip: The best description of the errorcodes is found here: + * http://www.directory-info.com/LDAP/LDAPErrorCodes.html + * + * @param int $errorcode Error code + * + * @return string The errorstring for the error. + */ function errorMessage($errorcode) { $errorMessages = array( @@ -843,36 +1116,38 @@ define ('NET_LDAP_ERROR', 1000); 0x5f => "LDAP_MORE_RESULTS_TO_RETURN", 0x60 => "LDAP_CLIENT_LOOP", 0x61 => "LDAP_REFERRAL_LIMIT_EXCEEDED", - 1000 => "Unknown Net_LDAP error" + 1000 => "Unknown Net_LDAP Error" ); - return isset($errorMessages[$errorcode]) ? $errorMessages[$errorcode] : $errorMessages[NET_LDAP_ERROR]; + return isset($errorMessages[$errorcode]) ? + $errorMessages[$errorcode] : + $errorMessages[NET_LDAP_ERROR] . ' (' . $errorcode . ')'; } /** - * Tell whether value is a Net_LDAP_Error or not - * - * @access public - * @param mixed - * @return boolean - */ - static function isError($value) + * Tell whether variable is a Net_LDAP_Error or not + * + * @param mixed $var A variable, most commonly some Net_LDAP* object + * + * @access public + * @return boolean + */ + function isError($var) { - return (is_a($value, "Net_LDAP_Error") || parent::isError($value)); + return (is_a($var, "Net_LDAP_Error") || parent::isError($var)); } /** - * gets a root dse object - * - * @access public - * @author Jan Wagner - * @param array Array of attributes to search for - * @return object mixed Net_LDAP_Error or Net_LDAP_RootDSE - */ + * Gets a rootDSE object + * + * @param array $attrs Array of attributes to search for + * + * @access public + * @author Jan Wagner + * @return Net_LDAP_Error|Net_LDAP_RootDSE Net_LDAP_Error or Net_LDAP_RootDSE object + */ function &rootDse($attrs = null) { - require_once('Net/LDAP/RootDSE.php'); - if (is_array($attrs) && count($attrs) > 0 ) { $attributes = $attrs; } else { @@ -885,120 +1160,190 @@ define ('NET_LDAP_ERROR', 1000); 'subschemaSubentry' ); } $result = $this->search('', '(objectClass=*)', array('attributes' => $attributes, 'scope' => 'base')); - if (Net_LDAP::isError($result)) return $result; - - $entry = $result->shift_entry(); - if (false === $entry) return $this->raiseError('Could not fetch RootDSE entry'); - - return new Net_LDAP_RootDSE($entry); + if (Net_LDAP::isError($result)) { + return $result; + } + $entry = $result->shiftEntry(); + if (false === $entry) { + return PEAR::raiseError('Could not fetch RootDSE entry'); + } + $ret = new Net_LDAP_RootDSE($entry); + return $ret; } /** - * alias function of rootDse() for perl-ldap interface - * - * @access public - * @see rootDse() - */ + * Alias function of rootDse() for perl-ldap interface + * + * @access public + * @see rootDse() + * @return Net_LDAP_Error|Net_LDAP_RootDSE + */ function &root_dse() { $args = func_get_args(); - return call_user_func_array(array($this, 'rootDse'), $args); + return call_user_func_array(array(&$this, 'rootDse'), $args); } /** - * get a schema object - * - * @access public - * @author Jan Wagner - * @param string Subschema entry dn - * @return object mixed Net_LDAP_Schema or Net_LDAP_Error - */ - function &schema($dn = null) - { - require_once('Net/LDAP/Schema.php'); - - $schema =& new Net_LDAP_Schema(); + * Get a schema object + * + * @param string $dn Subschema entry dn + * + * @access public + * @author Jan Wagner + * @return Net_LDAP_Schema|Net_LDAP_Error Net_LDAP_Schema or Net_LDAP_Error object + */ + function &schema($dn = null) + { + if (false == is_a($this->_schema, 'Net_LDAP_Schema')) { + $this->_schema = & new Net_LDAP_Schema(); + + if (is_null($dn)) { + // get the subschema entry via root dse + $dse = $this->rootDSE(array('subschemaSubentry')); + if (false == Net_LDAP::isError($dse)) { + $base = $dse->getValue('subschemaSubentry', 'single'); + if (!Net_LDAP::isError($base)) { + $dn = $base; + } + } + } - if (is_null($dn)) { - // get the subschema entry via root dse - $dse = $this->rootDSE(array('subschemaSubentry')); - if (false == Net_LDAP::isError($dse)) { - $base = $dse->getValue('subschemaSubentry', 'single'); - if (!Net_LDAP::isError($base)) { - $dn = $base; + // + // Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that incorrectly + // call this entry subSchemaSubentry instead of subschemaSubentry. + // Note the correct case/spelling as per RFC 2251. + // + if (is_null($dn)) { + // get the subschema entry via root dse + $dse = $this->rootDSE(array('subSchemaSubentry')); + if (false == Net_LDAP::isError($dse)) { + $base = $dse->getValue('subSchemaSubentry', 'single'); + if (!Net_LDAP::isError($base)) { + $dn = $base; + } } } - } - if (is_null($dn)) { - $dn = 'cn=Subschema'; - } - // fetch the subschema entry - $result = $this->search($dn, '(objectClass=*)', - array('attributes' => array_values($schema->types), 'scope' => 'base')); - if (Net_LDAP::isError($result)) { - return $result; - } + // + // Final fallback case where there is no subschemaSubentry attribute + // in the root DSE (this is a bug for an LDAP v3 server so report this + // to your LDAP vendor if you get this far). + // + if (is_null($dn)) { + $dn = 'cn=Subschema'; + } - $entry = $result->shift_entry(); - if (false === $entry) { - return $this->raiseError('Could not fetch Subschema entry'); - } + // fetch the subschema entry + $result = $this->search($dn, '(objectClass=*)', + array('attributes' => array_values($this->_schema->types), + 'scope' => 'base')); + if (Net_LDAP::isError($result)) { + return $result; + } - $schema->parse($entry); + $entry = $result->shiftEntry(); + if (false === $entry) { + return PEAR::raiseError('Could not fetch Subschema entry'); + } - return $schema; + $this->_schema->parse($entry); + } + return $this->_schema; } /** - * Encodes given attributes to UTF8 if needed - * - * This function takes attributes in an array and then checks against the schema if they need - * UTF8 encoding. If that is so, they will be encoded. An encoded array will be returned and - * can be used for adding or modifying. - * - * @access public - * @param array Array of attributes - * @return array Array of UTF8 encoded attributes - */ + * Checks if phps ldap-extension is loaded + * + * If it is not loaded, it tries to load it manually using PHPs dl(). + * It knows both windows-dll and *nix-so. + * + * @static + * @return Net_LDAP_Error|true + */ + function checkLDAPExtension() + { + if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) { + $msg = "It seems that you do not have the ldap-extension installed."; + $msg .= "Please install it before using the Net_LDAP package."; + return PEAR::raiseError($msg); + } else { + return true; + } + } + + /** + * Encodes given attributes to UTF8 if needed by schema + * + * This function takes attributes in an array and then checks against the schema + * if they need UTF8 encoding. If that is so, they will be encoded. An encoded + * array will be returned and can be used for adding or modifying. + * + * $attributes is expected to be an array with keys describing + * the attribute names and the values as the value of this attribute: + * + * $attributes = array('cn' => 'foo', 'attr2' => array('mv1', 'mv2')); + * + * + * @param array $attributes Array of attributes + * + * @access public + * @return array|Net_LDAP_Error Array of UTF8 encoded attributes or Error + */ function utf8Encode($attributes) { return $this->_utf8($attributes, 'utf8_encode'); } /** - * Decodes the given attribute values - * - * @access public - * @param array Array of attributes - * @return array Array with decoded attribute values - */ + * Decodes the given attribute values if needed by schema + * + * $attributes is expected to be an array with keys describing + * the attribute names and the values as the value of this attribute: + * + * $attributes = array('cn' => 'foo', 'attr2' => array('mv1', 'mv2')); + * + * + * @param array $attributes Array of attributes + * + * @access public + * @see utf8Encode() + * @return array|Net_LDAP_Error Array with decoded attribute values or Error + */ function utf8Decode($attributes) { return $this->_utf8($attributes, 'utf8_decode'); } /** - * Encodes or decodes attribute values if needed - * - * @access private - * @param array Array of attributes - * @param array Function to apply to attribute values - * @return array Array of attributes with function applied to values - */ + * Encodes or decodes attribute values if needed + * + * @param array $attributes Array of attributes + * @param array $function Function to apply to attribute values + * + * @access private + * @return array|Net_LDAP_Error Array of attributes with function + * applied to values or Error + */ function _utf8($attributes, $function) { + if (!is_array($attributes) || array_key_exists(0, $attributes)) { + $msg = 'Parameter $attributes is expected to be an associative array'; + return PEAR::raiseError($msg); + } + if (!$this->_schema) { $this->_schema = $this->schema(); } - if (!$this->_link || Net_LDAP::isError($this->_schema) || !function_exists($function)) { - return $attributes; + if (!$this->_link || Net_LDAP::isError($this->_schema) + || !function_exists($function)) { + return $attributes; } if (is_array($attributes) && count($attributes) > 0) { - foreach( $attributes as $k => $v ) { + foreach ($attributes as $k => $v) { if (!isset($this->_schemaAttrs[$k])) { @@ -1007,7 +1352,8 @@ define ('NET_LDAP_ERROR', 1000); continue; } - if (false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) { + $haystack = '1.3.6.1.4.1.1466.115.121.1.15'; + if (false !== strpos($attr['syntax'], $haystack)) { $encode = true; } else { $encode = false; @@ -1021,7 +1367,7 @@ define ('NET_LDAP_ERROR', 1000); if ($encode) { if (is_array($v)) { foreach ($v as $ak => $av) { - $v[$ak] = call_user_func($function, $av ); + $v[$ak] = call_user_func($function, $av); } } else { $v = call_user_func($function, $v); @@ -1032,34 +1378,60 @@ define ('NET_LDAP_ERROR', 1000); } return $attributes; } + + /** + * Get the LDAP link + * + * @access public + * @return resource LDAP link + */ + function &getLink() + { + return $this->_link; + } + } /** - * Net_LDAP_Error implements a class for reporting portable LDAP error messages. - * - * @package Net_LDAP - */ +* Net_LDAP_Error implements a class for reporting portable LDAP error messages. +* +* @category Net +* @package Net_LDAP +* @author Tarjej Huse +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ class Net_LDAP_Error extends PEAR_Error { /** * Net_LDAP_Error constructor. * - * @param mixed Net_LDAP error code, or string with error message. - * @param integer what "error mode" to operate in - * @param integer what error level to use for $mode & PEAR_ERROR_TRIGGER - * @param mixed additional debug info, such as the last query + * @param string $message String with error message. + * @param integer $code Net_LDAP error code + * @param integer $mode what "error mode" to operate in + * @param mixed $level what error level to use for $mode & + * PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query + * * @access public * @see PEAR_Error */ - function Net_LDAP_Error($code = NET_LDAP_ERROR, $mode = PEAR_ERROR_RETURN, - $level = E_USER_NOTICE, $debuginfo = null) + function Net_LDAP_Error($message = 'Net_LDAP_Error', $code = NET_LDAP_ERROR, + $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, + $debuginfo = null) { - $mode = PEAR_ERROR_RETURN; + $error_code = NET_LDAP_ERROR; + + $msg = "$message: $code"; + if (is_int($code)) { - $this->PEAR_Error('Net_LDAP_Error: ' . Net_LDAP::errorMessage($code), $code, $mode, $level, $debuginfo); - } else { - $this->PEAR_Error("Net_LDAP_Error: $code", NET_LDAP_ERROR, $mode, $level, $debuginfo); + $msg = $message . ': ' . Net_LDAP::errorMessage($code); + + $error_code = $code; } + + $this->PEAR_Error($msg, $error_code, $mode, $level, $debuginfo); } } + ?> diff --git a/thirdparty/pear/Net/LDAP/Entry.php b/thirdparty/pear/Net/LDAP/Entry.php old mode 100644 new mode 100755 index cae1c13..9f016a9 --- a/thirdparty/pear/Net/LDAP/Entry.php +++ b/thirdparty/pear/Net/LDAP/Entry.php @@ -1,524 +1,915 @@ +* @author Jan Wagner +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: Entry.php,v 1.58 2008/11/03 14:06:27 beni Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ + +require_once 'PEAR.php'; +require_once 'Util.php'; /** - * This class represents an LDAP entry - * - * @package Net_LDAP - * @author Tarjei Huse - * @version $Revision$ - */ +* Object representation of a directory entry +* +* This class represents a directory entry. You can add, delete, replace +* attributes and their values, rename the entry, delete the entry. +* +* @category Net +* @package Net_LDAP +* @author Jan Wagner +* @author Tarjej Huse +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ class Net_LDAP_Entry extends PEAR { - /**#@+ - * Array of the attributes - * - * @access private - * @var array - */ - var $_attrs = array(); - - /** - * Array of attributes to be deleted upon update() - */ - var $_delAttrs = array(); - - /** - * Array of attributes to be modified upon update() - */ - var $_modAttrs = array(); - - /** - * Array of attributes to be added upon update() - */ - var $_addAttrs = array(); - /**#@-*/ - - /** - * The distinguished name of the entry - * - * @access private - * @var string - */ - var $_dn = ''; - - /** - * LDAP resource link - * - * @access private - * @var resource - */ + /** + * Entry ressource identifier + * + * @access private + * @var ressourcee + */ + var $_entry = null; + + /** + * LDAP ressource identifier + * + * @access private + * @var ressource + */ var $_link = null; - - /** - * Value of old DN if DN has changed - * - * @access private - * @var string - */ - var $_olddn = ''; - - /**#@+ - * Array of errors for debugging class - * - * @access private - */ - var $_error = array(); - - /** - * updatechecks - */ - var $updateCheck = array('newdn' => false, - 'modify' => false, - 'newEntry' => true - ); // since the entry is not changed before the update(); - - /** - * Net_LDAP_Schema object TO BE REMOVED - */ - var $_schema; - /**#@-*/ - - /** Constructor - * - * @param - link - ldap_resource_link, dn = string entry dn, attributes - array entry attributes array. - * @return - none - **/ - function Net_LDAP_Entry($link = null, $dn = null, $attributes = null) + + /** + * Net_LDAP object + * + * This object will be used for updating and schema checking + * + * @access private + * @var object Net_LDAP + */ + var $_ldap = null; + + /** + * Distinguished name of the entry + * + * @access private + * @var string + */ + var $_dn = null; + + /** + * Attributes + * + * @access private + * @var array + */ + var $_attributes = array(); + + /** + * Original attributes before any modification + * + * @access private + * @var array + */ + var $_original = array(); + + + /** + * Map of attribute names + * + * @access private + * @var array + */ + var $_map = array(); + + + /** + * Is this a new entry? + * + * @access private + * @var boolean + */ + var $_new = true; + + /** + * New distinguished name + * + * @access private + * @var string + */ + var $_newdn = null; + + /** + * Shall the entry be deleted? + * + * @access private + * @var boolean + */ + var $_delete = false; + + /** + * Map with changes to the entry + * + * @access private + * @var array + */ + var $_changes = array("add" => array(), + "delete" => array(), + "replace" => array() + ); + /** + * Internal Constructor + * + * Constructor of the entry. Sets up the distinguished name and the entries + * attributes. + * You should not call this method manually! Use {@link Net_LDAP_Entry::createFresh()} instead! + * + * @param Net_LDAP|ressource|array &$ldap Net_LDAP object, ldap-link ressource or array of attributes + * @param string|ressource $entry Either a DN or a LDAP-Entry ressource + * + * @access protected + * @return none + */ + function Net_LDAP_Entry(&$ldap, $entry = null) { - if (!is_null($link)) { - $this->_link = $link; + $this->PEAR('Net_LDAP_Error'); + + if (is_resource($entry)) { + $this->_entry = &$entry; + } else { + $this->_dn = $entry; } - if (!is_null($dn)) { - $this->_set_dn($dn); + + if (is_a($ldap, 'Net_LDAP')) { + $this->_ldap = &$ldap; + $this->_link = $ldap->getLink(); + } elseif (is_resource($ldap)) { + $this->_link = $ldap; + } elseif (is_array($ldap)) { + $this->_setAttributes($ldap); // setup attrs manually } - if (is_array($attributes) && count($attributes) > 0) { - $this->_set_attributes($attributes); - } else { - $this->updateCheck['newEntry'] = true; + + if (is_resource($this->_entry) && is_resource($this->_link)) { + $this->_new = false; + $this->_dn = @ldap_get_dn($this->_link, $this->_entry); + $this->_setAttributes(); // fetch attributes from server } } - - /** - * Set the reasourcelink to the ldapserver. - * - * @access private - * @param resource LDAP link - */ - function _set_link(&$link) + + /** + * Creates a fresh entry that may be added to the directory later on + * + * Use this method, if you want to initialize a fresh entry. + * + * The method should be called statically: $entry = Net_LDAP_Entry::createFresh(); + * You should put a 'objectClass' attribute into the $attrs so the directory server + * knows which object you want to create. However, you may omit this in case you + * don't want to add this entry to a directory server. + * + * The attributes parameter is as following: + * + * $attrs = array( 'attribute1' => array('value1', 'value2'), + * 'attribute2' => 'single value' + * ); + * + * + * @param string $dn DN of the Entry + * @param array $attrs Attributes of the entry + * + * @static + * @return Net_LDAP_Entry + */ + function createFresh($dn, $attrs = array()) { - $this->_link = $link; + if (!is_array($attrs)) { + return PEAR::raiseError("Unable to create fresh entry: Parameter \$attrs needs to be an array!"); + } + + $entry = new Net_LDAP_Entry($attrs, $dn); + return $entry; } - - /** - * set the entrys DN - * - * @access private - * @param string - */ - function _set_dn($dn) + + /** + * Get or set the distinguished name of the entry + * + * If called without an argument the current (or the new DN if set) DN gets returned. + * If you provide an DN, this entry is moved to the new location specified if a DN existed. + * If the DN was not set, the DN gets initialized. Call {@link update()} to actually create + * the new Entry in the directory. + * To fetch the current active DN after setting a new DN but before an update(), you can use + * {@link currentDN()} to retrieve the DN that is currently active. + * + * Please note that special characters (eg german umlauts) should be encoded using utf8_encode(). + * You may use {@link Net_LDAP_Util::canonical_dn()} for properly encoding of the DN. + * + * @param string $dn New distinguished name + * + * @access public + * @return string|true Distinguished name (or true if a new DN was provided) + */ + function dn($dn = null) { - $this->_dn = $dn; + if (false == is_null($dn)) { + if (is_null($this->_dn)) { + $this->_dn = $dn; + } else { + $this->_newdn = $dn; + } + return true; + } + return (isset($this->_newdn) ? $this->_newdn : $this->currentDN()); } /** - * sets the internal array of the entrys attributes. - * - * @access private - * @param array - */ - function _set_attributes($attributes= array()) + * Renames or moves the entry + * + * This is just a convinience alias to {@link dn()} + * to make your code more meaningful. + * + * @param string $newdn The new DN + * @return true + */ + function move($newdn) { - $this->_attrs = $attributes; - // this is the sign that the entry exists in the first place: - $this->updateCheck['newEntry'] = false; + return $this->dn($newdn); } - /** - * removes [count] entries from the array. - * - * remove all the count elements in the array: - * Used before ldap_modify, ldap_add - * + /** + * Sets the internal attributes array + * + * This fetches the values for the attributes from the server. + * The attribute Syntax will be checked so binary attributes will be returned + * as binary values. + * + * Attributes may be passed directly via the $attributes parameter to setup this + * entry manually. This overrides attribute fetching from the server. + * + * @param array $attributes Attributes to set for this entry + * * @access private - * @return array Cleaned array of attributes + * @return void */ - function _clean_entry() + function _setAttributes($attributes = null) { - $attributes = array(); - - for ($i=0; $i < $this->_attrs['count'] ; $i++) { - - $attr = $this->_attrs[$i]; - - if ($this->_attrs[$attr]['count'] == 1) { - $attributes[$this->_attrs[$i]] = $this->_attrs[$attr][0]; - } else { - $attributes[$attr] = $this->_attrs[$attr]; - unset ($attributes[ $attr ]['count']); + /* + * fetch attributes from the server + */ + if (is_null($attributes) && is_resource($this->_entry) && is_resource($this->_link)) { + // fetch schema + if (is_a($this->_ldap, 'Net_LDAP')) { + $schema =& $this->_ldap->schema(); + } + // fetch attributes + $attributes = array(); + do { + if (empty($attr)) { + $ber = null; + $attr = @ldap_first_attribute($this->_link, $this->_entry, $ber); + } else { + $attr = @ldap_next_attribute($this->_link, $this->_entry, $ber); + } + if ($attr) { + $func = 'ldap_get_values'; // standard function to fetch value + + // Try to get binary values as binary data + if (is_a($schema, 'Net_LDAP_Schema')) { + if ($schema->isBinary($attr)) { + $func = 'ldap_get_values_len'; + } + } + // fetch attribute value (needs error checking?) + $attributes[$attr] = $func($this->_link, $this->_entry, $attr); + } + } while ($attr); + } + + /* + * set attribute data directly, if passed + */ + if (is_array($attributes) && count($attributes) > 0) { + if (isset($attributes["count"]) && is_numeric($attributes["count"])) { + unset($attributes["count"]); + } + foreach ($attributes as $k => $v) { + // attribute names should not be numeric + if (is_numeric($k)) { + continue; + } + // map generic attribute name to real one + $this->_map[strtolower($k)] = $k; + // attribute values should be in an array + if (false == is_array($v)) { + $v = array($v); + } + // remove the value count (comes from ldap server) + if (isset($v["count"])) { + unset($v["count"]); + } + $this->_attributes[$k] = $v; } } - - return $attributes; + // save a copy for later use + $this->_original = $this->_attributes; } - /** - * returns an assosiative array of all the attributes in the array + /** + * Get the values of all attributes in a hash * - * attributes - returns an assosiative array of all the attributes in the array - * of the form array ('attributename'=>'singelvalue' , 'attribute'=>array('multiple','values')) + * The returned hash has the form + * array('attributename' => 'single value', + * 'attributename' => array('value1', value2', value3')) * - * @param none - * @return array Array of attributes and values. + * @access public + * @return array Hash of all attributes with their values */ - function attributes() + function getValues() { - return $this->_clean_entry(); + $attrs = array(); + foreach ($this->_attributes as $attr => $value) { + $attrs[$attr] = $this->getValue($attr); + } + return $attrs; } - /** - * Add one or more attribute to the entry + /** + * Get the value of a specific attribute + * + * The first parameter is the name of the attribute + * The second parameter influences the way the value is returned: + * 'single': only the first value is returned as string + * 'all': all values including the value count are returned in an + * array + * 'default': in all other cases an attribute value with a single value is + * returned as string, if it has multiple values it is returned + * as an array (without value count) * - * The values given will be added to the values which already exist for the given attributes. - * usage: - * $entry->add ( array('sn'=>'huse',objectclass=>array(top,posixAccount))) + * @param string $attr Attribute name + * @param string $option Option * - * @param array Array of attributes - * @return mixed Net_Ldap_Error if error, else true. + * @access public + * @return string|array|PEAR_Error string, array or PEAR_Error */ - function add($attr = array()) + function getValue($attr, $option = null) { - if (!isset($this->_attrs['count'])) { - $this->_attrs['count'] = 0; - } - if (!is_array($attr)) { - return $this->raiseError("Net_LDAP::add : the parameter supplied is not an array, $attr", 1000); - } - /* if you passed an empty array, that is your problem! */ - if (count ($attr)==0) { - return true; + $attr = $this->_getAttrName($attr); + + if (false == array_key_exists($attr, $this->_attributes)) { + return PEAR::raiseError("Unknown attribute ($attr) requested"); } - foreach ($attr as $k => $v ) { - // empty entrys should not be added to the entry. - if ($v == '') { - continue; - } - if ($this->exists($k)) { - if (!is_array($this->_attrs[$k])) { - return $this->raiseError("Possible malformed array as parameter to Net_LDAP::add()."); - } - array_push($this->_attrs[$k],$v); - $this->_attrs[$k]['count']++; - } else { - $this->_attrs[$k][0] = $v; - $this->_attrs[$k]['count'] = 1; - $this->_attrs[$this->_attrs['count']] = $k; - $this->_attrs['count']++; - } - // Fix for bug #952 - if (empty($this->_addAttrs[$k])) { - $this->_addAttrs[$k] = array(); - } - if (false == is_array($v)) { - $v = array($v); - } - foreach ($v as $value) { - array_push($this->_addAttrs[$k], $value); - } + $value = $this->_attributes[$attr]; + + if ($option == "single" || (count($value) == 1 && $option != 'all')) { + $value = array_shift($value); } - return true; + + return $value; } - /** - * Set or get the DN for the object + /** + * Alias function of getValue for perl-ldap interface * - * If a new dn is supplied, this will move the object when running $obj->update(); + * @see getValue() + * @return string|array|PEAR_Error + */ + function get_value() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'getValue' ), $args); + } + + /** + * Returns an array of attributes names * - * @param string DN + * @access public + * @return array Array of attribute names */ - function dn($newdn = '') + function attributes() { - if ($newdn == '') { - return $this->_dn; - } - - $this->_olddn = $this->_dn; - $this->_dn = $newdn; - $this->updateCheck['newdn'] = true; + return array_keys($this->_attributes); } - - /** - * check if a certain attribute exists in the directory + + /** + * Returns whether an attribute exists or not * - * @param string attribute name. + * @param string $attr Attribute name + * + * @access public * @return boolean */ function exists($attr) { - if (array_key_exists($attr, $this->_attrs)) { - return true; - } - return false; + $attr = $this->_getAttrName($attr); + return array_key_exists($attr, $this->_attributes); } - /** - * get_value get the values for a attribute + /** + * Adds a new attribute or a new value to an existing attribute + * + * The paramter has to be an array of the form: + * array('attributename' => 'single value', + * 'attributename' => array('value1', 'value2)) + * When the attribute already exists the values will be added, else the + * attribute will be created. These changes are local to the entry and do + * not affect the entry on the server until update() is called. * - * returns either an array or a string - * possible values for option: - * alloptions - returns an array with the values + a countfield. - * i.e.: array (count=>1, 'sn'=>'huse'); - * single - returns the, first value in the array as a string. + * Note, that you can add values of attributes that you haven't selected, but if + * you do so, {@link getValue()} and {@link getValues()} will only return the + * values you added, _NOT_ all values present on the server. To avoid this, just refetch + * the entry after calling {@link update()} or select the attribute. * - * @param $attr string attribute name - * @param $options array + * @param array $attr Attributes to add + * + * @access public + * @return true|Net_LDAP_Error */ - function get_value($attr = '', $options = '') + function add($attr = array()) { - if (array_key_exists($attr, $this->_attrs)) { - - if ($options == 'single') { - if (is_array($this->_attrs[$attr])) { - return $this->_attrs[$attr][0]; + if (false == is_array($attr)) { + return PEAR::raiseError("Parameter must be an array"); + } + foreach ($attr as $k => $v) { + $k = $this->_getAttrName($k); + if (false == is_array($v)) { + // Do not add empty values + if ($v == null) { + continue; } else { - return $this->_attrs[$attr]; + $v = array($v); } } + // add new values to existing attribute or add new attribute + if ($this->exists($k)) { + $this->_attributes[$k] = array_unique(array_merge($this->_attributes[$k], $v)); + } else { + $this->_map[strtolower($k)] = $k; + $this->_attributes[$k] = $v; + } - $value = $this->_attrs[$attr]; - - if (!$options == 'alloptions') { - unset ($value['count']); + // save changes for update() + if (empty($this->_changes["add"][$k])) { + $this->_changes["add"][$k] = array(); } - return $value; - } else { - return ''; + $this->_changes["add"][$k] = array_unique(array_merge($this->_changes["add"][$k], $v)); } + $return = true; + return $return; } /** - * add/delete/modify attributes - * - * this function tries to do all the things that replace(),delete() and add() does on an object. - * Syntax: - * array ( 'attribute' => newval, 'delattribute' => '', newattrivute => newval); - * Note: You cannot use this function to modify parts of an attribute. You must modify the whole attribute. - * You may call the function many times before running $entry->update(); - * - * @param array attributes to be modified - * @return mixed errorObject if failure, true if success. - */ - function modify($attrs = array()) { - - if (!is_array($attrs) || count ($attrs) < 1 ) { - return $this->raiseError("You did not supply an array as expected",1000); + * Deletes an whole attribute or a value or the whole entry + * + * The parameter can be one of the following: + * + * "attributename" - The attribute as a whole will be deleted + * array("attributename1", "attributename2) - All given attributes will be + * deleted + * array("attributename" => "value") - The value will be deleted + * array("attributename" => array("value1", "value2") - The given values + * will be deleted + * If $attr is null or omitted , then the whole Entry will be deleted! + * + * These changes are local to the entry and do + * not affect the entry on the server until {@link update()} is called. + * + * Please note that you must select the attribute (at $ldap->search() for example) + * to be able to delete values of it, Otherwise {@link update()} will silently fail + * and remove nothing. + * + * @param string|array $attr Attributes to delete (NULL or missing to delete whole entry) + * + * @access public + * @return true + */ + function delete($attr = null) + { + if (is_null($attr)) { + $this->_delete = true; + return true; } - - foreach ($attrs as $k => $v) { - // empty values are deleted (ldap v3 handling is in update() ) - if ($v == '' && $this->exists($k)) { - $this->_delAttrs[$k] = ''; - continue; - } - /* existing attributes are modified*/ - if ($this->exists($k) ) { - if (is_array($v)) { - $this->_modAttrs[$k] = $v; + if (is_string($attr)) { + $attr = array($attr); + } + // Make the assumption that attribute names cannot be numeric, + // therefore this has to be a simple list of attribute names to delete + if (is_numeric(key($attr))) { + foreach ($attr as $name) { + if (is_array($name)) { + // someone mixed modes (list mode but specific values given!) + $del_attr_name = array_search($name, $attr); + $this->delete(array($del_attr_name => $name)); } else { - $this->_modAttrs[$k][0] = $v; - } - } else { - /* new ones are created */ - if (is_array($v) ) { - // an empty array is deleted... - if (count($v) == 0 ) { - $this->_delAttrs[$k] = ''; - } else { - $this->_addAttrs[$k] = $v; + $name = $this->_getAttrName($name); + if ($this->exists($name)) { + $this->_changes["delete"][$name] = null; + unset($this->_attributes[$name]); } + } + } + } else { + // Here we have a hash with "attributename" => "value to delete" + foreach ($attr as $name => $values) { + if (is_int($name)) { + // someone mixed modes and gave us just an attribute name + $this->delete($values); } else { - // dont't add empty attributes - if ($v != null) $this->_addAttrs[$k][0] = $v; + // get the correct attribute name + $name = $this->_getAttrName($name); + if ($this->exists($name)) { + if (false == is_array($values)) { + $values = array($values); + } + // save values to be deleted + if (empty($this->_changes["delete"][$name])) { + $this->_changes["delete"][$name] = array(); + } + $this->_changes["delete"][$name] = + array_unique(array_merge($this->_changes["delete"][$name], $values)); + foreach ($values as $value) { + // find the key for the value that should be deleted + $key = array_search($value, $this->_attributes[$name]); + if (false !== $key) { + // delete the value + unset($this->_attributes[$name][$key]); + } + } + } } - } + } } - return true; + $return = true; + return $return; } - - /** - * replace a certain attributes value + /** + * Replaces attributes or its values + * + * The parameter has to an array of the following form: + * array("attributename" => "single value", + * "attribute2name" => array("value1", "value2")) + * If the attribute does not yet exist it will be added instead. + * If the attribue value is null, the attribute will de deleted + * + * These changes are local to the entry and do + * not affect the entry on the server until {@link update()} is called. * - * replace - replace a certain attributes value - * example: - * $entry->replace(array('uid'=>array('tarjei'))); + * @param array $attr Attributes to replace * - * @param array attributes to be replaced - * @return mixed error if failure, true if sucess. + * @access public + * @return true|Net_LDAP_Error */ - function replace($attrs = array() ) + function replace($attr = array()) { - foreach ($attrs as $k => $v) { - - if ($this->exists($k)) { - - if (is_array($v)) { - $this->_attrs[$k] = $v; - $this->_attrs[$k]['count'] = count($v); - $this->_modAttrs[$k] = $v; + if (false == is_array($attr)) { + return PEAR::raiseError("Parameter must be an array"); + } + foreach ($attr as $k => $v) { + $k = $this->_getAttrName($k); + if (false == is_array($v)) { + // delete attributes with empty values + if ($v == null) { + $this->delete($k); + continue; } else { - $this->_attrs[$k]['count'] = 1; - $this->_attrs[$k][0] = $v; - $this->_modAttrs[$k][0] = $v; + $v = array($v); } + } + // existing attributes will get replaced + if ($this->exists($k)) { + $this->_changes["replace"][$k] = $v; + $this->_attributes[$k] = $v; } else { - return $this->raiseError("Attribute $k does not exist",16); // 16 = no such attribute exists. + // new ones just get added + $this->add(array($k => $v)); } } - return true; + $return = true; + return $return; } - /** - * delete attributes + /** + * Update the entry on the directory server * - * Use this function to delete certain attributes from an object. + * @param Net_LDAP $ldap If passed, a call to setLDAP() is issued prior update, thus switching the LDAP-server. This is for perl-ldap interface compliance * - * @param - array of attributes to be deleted - * @return mixed Net_Ldap_Error if failure, true if success. + * @access public + * @return true|Net_LDAP_Error + * @todo Entry rename with a DN containing special characters needs testing! */ - function delete($attrs = array()) + function update($ldap = null) { - foreach ($attrs as $k => $v) { - - if ($this->exists ($k)) { - // if v is a null, then remove the whole attribute, else only the value. - if ($v == '') { - unset($this->_attrs[$k]); - $this->_delAttrs[$k] = ""; - // else we remove only the correct value. - } else { - for ($i = 0;$i< $this->_attrs[$k]['count'];$i++) { - if ($this->_attrs[$k][$i] == $v ) { - unset ($this->_attrs[$k][$i]); - $this->_delAttrs[$k] = $v; - continue; - } - } - } + if ($ldap) { + $msg = $this->setLDAP($ldap); + if (Net_LDAP::isError($msg)) { + return PEAR::raiseError('You passed an invalid $ldap variable to update()'); + } + } + + // ensure we have a valid LDAP object + $ldap =& $this->getLDAP(); + if (!is_a($ldap, 'Net_LDAP')) { + return PEAR::raiseError("The entries LDAP object is not valid"); + } + + // Get and check link + $link = $ldap->getLink(); + if (!is_resource($link)) { + return PEAR::raiseError("Could not update entry: internal LDAP link is invalid"); + } + + /* + * Delete the entry + */ + if (true === $this->_delete) { + return $ldap->delete($this); + } + + /* + * New entry + */ + if (true === $this->_new) { + $msg = $ldap->add($this); + if (Net_LDAP::isError($msg)) { + return $msg; + } + $this->_new = false; + $this->_changes['add'] = array(); + $this->_changes['delete'] = array(); + $this->_changes['replace'] = array(); + $this->_original = $this->_attributes; + + $return = true; + return $return; + } + + /* + * Rename/move entry + */ + if (false == is_null($this->_newdn)) { + if ($ldap->getLDAPVersion() !== 3) { + return PEAR::raiseError("Renaming/Moving an entry is only supported in LDAPv3"); + } + // make dn relative to parent (needed for ldap rename) + $parent = Net_LDAP_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false)); + if (Net_LDAP::isError($parent)) { + return $parent; + } + $child = array_shift($parent); + // maybe the dn consist of a multivalued RDN, we must build the dn in this case + // because the $child-RDN is an array! + if (is_array($child)) { + $child = Net_LDAP_Util::canonical_dn($child); + } + $parent = Net_LDAP_Util::canonical_dn($parent); + + // rename/move + if (false == @ldap_rename($link, $this->_dn, $child, $parent, true)) { + return PEAR::raiseError("Entry not renamed: " . + @ldap_error($link), @ldap_errno($link)); + } + // reflect changes to local copy + $this->_dn = $this->_newdn; + $this->_newdn = null; + } + + /* + * Carry out modifications to the entry + */ + // ADD + foreach ($this->_changes["add"] as $attr => $value) { + // if attribute exists, add new values + if ($this->exists($attr)) { + if (false === @ldap_mod_add($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not add new values to attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } } else { - $this->raiseError("You tried to delete a nonexisting attribute!",16); + // new attribute + if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not add new attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + } + // all went well here, I guess + unset($this->_changes["add"][$attr]); + } + + // DELETE + foreach ($this->_changes["delete"] as $attr => $value) { + // In LDAPv3 you need to specify the old values for deleting + if (is_null($value) && $ldap->getLDAPVersion() === 3) { + $value = $this->_original[$attr]; } - } - return true; + if (false === @ldap_mod_del($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not delete attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + unset($this->_changes["delete"][$attr]); + } + + // REPLACE + foreach ($this->_changes["replace"] as $attr => $value) { + if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not replace attribute $attr values: " . + @ldap_error($link), @ldap_errno($link)); + } + unset($this->_changes["replace"][$attr]); + } + + // all went well, so _original (server) becomes _attributes (local copy) + $this->_original = $this->_attributes; + + $return = true; + return $return; } - /** - * update the Entry in LDAP + /** + * Returns the right attribute name * - * After modifying an object, you must run update() to - * make the updates on the ldap server. Before that, they only exists in the object. + * @param string $attr Name of attribute * - * @param object Net_LDAP - * @return mixed Net_LDAP_Error object on failure or true on success + * @access private + * @return string The right name of the attribute */ - function update ($ldapObject = null) + function _getAttrName($attr) { - if ($ldapObject == null && $this->_link == null ) { - $this->raiseError("No link to database"); + $name = strtolower($attr); + if (array_key_exists($name, $this->_map)) { + $attr = $this->_map[$name]; } + return $attr; + } - if ($ldapObject != null) { - $this->_link =& $ldapObject->_link; + /** + * Returns a reference to the LDAP-Object of this entry + * + * @access public + * @return Net_LDAP|Net_LDAP_Error Reference to the Net_LDAP Object (the connection) or Net_LDAP_Error + */ + function &getLDAP() + { + if (!is_a($this->_ldap, 'Net_LDAP')) { + $err = new PEAR_Error('LDAP is not a valid Net_LDAP object'); + return $err; + } else { + return $this->_ldap; } + } - //if it's a new - if ($this->updateCheck['newdn'] && !$this->updateCheck['newEntry']) { - if (@ldap_get_option( $this->_link, LDAP_OPT_PROTOCOL_VERSION, $version) && $version != 3) { - return $this->raiseError("Moving or renaming an dn is only supported in LDAP V3!", 80); - } - - $newparent = ldap_explode_dn($this->_dn, 0); - unset($newparent['count']); - $relativeDn = array_shift($newparent); - $newparent = join(',', $newparent); - - if (!@ldap_rename($this->_link, $this->_olddn, $relativeDn, $newparent, true)) { - return $this->raiseError("DN not renamed: " . ldap_error($this->_link), ldap_errno($this->_link)); - } + /** + * Sets a reference to the LDAP-Object of this entry + * + * After setting a Net_LDAP object, calling update() will use that object for + * updating directory contents. Use this to dynamicly switch directorys. + * + * @param Net_LDAP &$ldap Net_LDAP object that this entry should be connected to + * + * @access public + * @return true|Net_LDAP_Error + */ + function setLDAP(&$ldap) + { + if (!is_a($ldap, 'Net_LDAP')) { + return PEAR::raiseError("LDAP is not a valid Net_LDAP object"); + } else { + $this->_ldap =& $ldap; + return true; } + } - if ($this->updateCheck['newEntry']) { - //print "
    "; print_r($this->_clean_entry()); + /** + * Marks the entry as new. + * + * If an Entry is marked as new, it will be added to the directory when + * calling {@link update()}. This method is mainly intendet for internal + * Net_LDAP package usage, so if you use it, use it with care. + * + * @access private + * @param boolean $mark Value to set, defaults to "true" + */ + function _markAsNew($mark = true) + { + $this->_new = ($mark)? true : false; + } - if (!@ldap_add($this->_link, $this->dn(), $this->_clean_entry()) ) { - return $this->raiseError("Entry" . $this->dn() . " not added!" . - ldap_error($this->_link), ldap_errno($this->_link)); - } else { - return true; - } - // update existing entry + /** + * Applies a regular expression onto a single- or multivalued attribute (like preg_match()) + * + * This method behaves like PHPs preg_match() but with some exceptions. + * If you want to retrieve match information, then you MUST pass the + * $matches parameter via reference! otherwise you will get no matches. + * Since it is possible to have multi valued attributes the $matches + * array will have a additionally numerical dimension (one for each value): + * + * $matches = array( + * 0 => array (usual preg_match() returnarray), + * 1 => array (usual preg_match() returnarray) + * ) + * + * Please note, that $matches will be initialized to an empty array inside. + * + * Usage example: + * + * $result = $entry->preg_match('/089(\d+)/', 'telephoneNumber', &$matches); + * if ( $result === true ){ + * echo "First match: ".$matches[0][1]; // Match of value 1, content of first bracket + * } else { + * if ( Net_LDAP::isError($result) ) { + * echo "Error: ".$result->getMessage(); + * } else { + * echo "No match found."; + * } + * } + * + * + * Please note that it is important to test for an Net_LDAP_Error, because objects are + * evaluating to true by default, thus if a error occured, and you only check using "==" then + * you get misleading results. Use the "identical" (===) operator to test for matches to + * avoid this as shown above. + * + * @param string $regex The regular expression + * @param string $attr_name The attribute to search in + * @param array $matches (optional, PASS BY REFERENCE!) Array to store matches in + * + * @return boolean|Net_LDAP_Error TRUE, if we had a match in one of the values, otherwise false. Net_LDAP_Error in case something went wrong + */ + function preg_match($regex, $attr_name, $matches = array()) + { + $matches = array(); + + // fetch attribute values + $attr = $this->getValue($attr_name, 'all'); + if (Net_LDAP::isError($attr)) { + return $attr; } else { - $this->_error['first'] = $this->_modAttrs; - $this->_error['count'] = count($this->_modAttrs); - - // modified attributes - if (( count($this->_modAttrs)>0) && - !ldap_modify($this->_link, $this->dn(), $this->_modAttrs)) - { - return $this->raiseError("Entry " . $this->dn() . " not modified(attribs not modified): " . - ldap_error($this->_link),ldap_errno($this->_link)); - } - - // attributes to be deleted - if (( count($this->_delAttrs) > 0 )) - { - // in ldap v3 we need to supply the old attribute values for deleting - if (@ldap_get_option( $this->_link, LDAP_OPT_PROTOCOL_VERSION, $version) && $version == 3) { - foreach ( $this->_delAttrs as $k => $v ) { - if ( $v == '' && $this->exists($k) ) { - $this->_delAttrs[$k] = $this->get_value( $k ); - } - } - } - if ( !ldap_mod_del($this->_link, $this->dn(), $this->_delAttrs) ) { - return $this->raiseError("Entry " . $this->dn() . " not modified (attributes not deleted): " . - ldap_error($this->_link),ldap_errno($this->_link)); - } + unset($attr['count']); + } + + // perform preg_match() on all values + $match = false; + foreach ($attr as $thisvalue) { + $matches_int = array(); + if (preg_match($regex, $thisvalue, $matches_int)) { + $match = true; + array_push($matches, $matches_int); // store matches in reference } - - // new attributes - if ((count($this->_addAttrs)) > 0 && !ldap_modify($this->_link, $this->dn(), $this->_addAttrs)) { - return $this->raiseError( "Entry " . $this->dn() . " not modified (attributes not added): " . - ldap_error($this->_link),ldap_errno($this->_link)); - } - return true; } + return $match; + } + + /** + * Is this entry going to be deleted once update() is called? + * + * @return boolean + */ + function willBeDeleted() + { + return $this->_delete; + } + + /** + * Is this entry going to be moved once update() is called? + * + * @return boolean + */ + function willBeMoved() + { + return ($this->dn() !== $this->currentDN()); + } + + /** + * Returns always the original DN + * + * If an entry will be moved but {@link update()} was not called, + * {@link dn()} will return the new DN. This method however, returns + * always the current active DN. + * + * @return string + */ + function currentDN() + { + return $this->_dn; } -} + /** + * Returns the attribute changes to be carried out once update() is called + * + * @return array + */ + function getChanges() + { + return $this->_changes; + } +} ?> diff --git a/thirdparty/pear/Net/LDAP/Filter.php b/thirdparty/pear/Net/LDAP/Filter.php new file mode 100644 index 0000000..c151bde --- /dev/null +++ b/thirdparty/pear/Net/LDAP/Filter.php @@ -0,0 +1,438 @@ + +* $filter0 = Net_LDAP_Filter::create('stars', 'equals', '***'); +* $filter_not0 = Net_LDAP_Filter::combine('not', $filter0); +* +* $filter1 = Net_LDAP_Filter::create('gn', 'begins', 'bar'); +* $filter2 = Net_LDAP_Filter::create('gn', 'ends', 'baz'); +* $filter_comp = Net_LDAP_Filter::combine('or',array($filter_not0, $filter1, $filter2)); +* +* echo $filter_comp->asString(); +* // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar*)(gn=*baz)) +* // The stars in $filter0 are treaten as real stars unless you disable escaping. +*
    +* +* @category Net +* @package Net_LDAP +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: Filter.php,v 1.27 2008/06/04 06:12:04 beni Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ +class Net_LDAP_Filter extends PEAR +{ + /** + * Storage for combination of filters + * + * This variable holds a array of filter objects + * that should be combined by this filter object. + * + * @access private + * @var array + */ + var $_subfilters = array(); + + /** + * Match of this filter + * + * If this is a leaf filter, then a matching rule is stored, + * if it is a container, then it is a logical operator + * + * @access private + * @var string + */ + var $_match; + + /** + * Single filter + * + * If we operate in leaf filter mode, + * then the constructing method stores + * the filter representation here + * + * @acces private + * @var string + */ + var $_filter; + + /** + * Create a new Net_LDAP_Filter object and parse $filter. + * + * This is for PERL Net::LDAP interface. + * Construction of Net_LDAP_Filter objects should happen through either + * {@link create()} or {@link combine()} which give you more control. + * However, you may use the perl iterface if you already have generated filters. + * + * @param string $filter LDAP filter string + * + * @see parse() + */ + function Net_LDAP_Filter($filter = false) + { + // The optional parameter must remain here, because otherwise create() crashes + if (false !== $filter) { + $filter_o = Net_LDAP_Filter::parse($filter); + if (PEAR::isError($filter_o)) { + $this->_filter = $filter_o; // assign error, so asString() can report it + } else { + $this->_filter = $filter_o->asString(); + } + } + } + + /** + * Constructor of a new part of a LDAP filter. + * + * The following matching rules exists: + * - equals: One of the attributes values is exactly $value + * Please note that case sensitiviness is depends on the + * attributes syntax configured in the server. + * - begins: One of the attributes values must begin with $value + * - ends: One of the attributes values must end with $value + * - contains: One of the attributes values must contain $value + * - any: The attribute can contain any value but must be existent + * - greater: The attributes value is greater than $value + * - less: The attributes value is less than $value + * - greaterOrEqual: The attributes value is greater or equal than $value + * - lessOrEqual: The attributes value is less or equal than $value + * - approx: One of the attributes values is similar to $value + * + * If $escape is set to true (default) then $value will be escaped + * properly. If it is set to false then $value will be treaten as raw value. + * + * Examples: + * + * // This will find entries that contain an attribute "sn" that ends with "foobar": + * $filter = new Net_LDAP_Filter('sn', 'ends', 'foobar'); + * + * // This will find entries that contain an attribute "sn" that has any value set: + * $filter = new Net_LDAP_Filter('sn', 'any'); + * + * + * @param string $attr_name Name of the attribute the filter should apply to + * @param string $match Matching rule (equals, begins, ends, contains, greater, less, greaterOrEqual, lessOrEqual, approx, any) + * @param string $value (optional) if given, then this is used as a filter + * @param boolean $escape Should $value be escaped? (default: yes, see {@link Net_LDAP_Util::escape_filter_value()} for detailed information) + * + * @return Net_LDAP_Filter|Net_LDAP_Error + */ + function &create($attr_name, $match, $value = '', $escape = true) + { + $leaf_filter = new Net_LDAP_Filter(); + if ($escape) { + $array = Net_LDAP_Util::escape_filter_value(array($value)); + $value = $array[0]; + } + switch (strtolower($match)) { + case 'equals': + $leaf_filter->_filter = '(' . $attr_name . '=' . $value . ')'; + break; + case 'begins': + $leaf_filter->_filter = '(' . $attr_name . '=' . $value . '*)'; + break; + case 'ends': + $leaf_filter->_filter = '(' . $attr_name . '=*' . $value . ')'; + break; + case 'contains': + $leaf_filter->_filter = '(' . $attr_name . '=*' . $value . '*)'; + break; + case 'greater': + $leaf_filter->_filter = '(' . $attr_name . '>' . $value . ')'; + break; + case 'less': + $leaf_filter->_filter = '(' . $attr_name . '<' . $value . ')'; + break; + case 'greaterorequal': + $leaf_filter->_filter = '(' . $attr_name . '>=' . $value . ')'; + break; + case 'lessorequal': + $leaf_filter->_filter = '(' . $attr_name . '<=' . $value . ')'; + break; + case 'approx': + $leaf_filter->_filter = '(' . $attr_name . '~=' . $value . ')'; + break; + case 'any': + $leaf_filter->_filter = '(' . $attr_name . '=*)'; + break; + default: + return PEAR::raiseError('Net_LDAP_Filter create error: matching rule "' . $match . '" not known!'); + } + return $leaf_filter; + } + + /** + * Combine two or more filter objects using a logical operator + * + * This static method combines two or more filter objects and returns one single + * filter object that contains all the others. + * Call this method statically: $filter =& Net_LDAP_Filter('or', array($filter1, $filter2)) + * If the array contains filter strings instead of filter objects, we will try to parse them. + * + * @param string $log_op The locicall operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!" + * @param array|Net_LDAP_Filter $filters array with Net_LDAP_Filter objects + * + * @return Net_LDAP_Filter|Net_LDAP_Error + * @static + */ + function &combine($log_op, $filters) + { + if (PEAR::isError($filters)) { + return $filters; + } + + // substitude named operators to logical operators + if ($log_op == 'and') $log_op = '&'; + if ($log_op == 'or') $log_op = '|'; + if ($log_op == 'not') $log_op = '!'; + + // tests for sane operation + if ($log_op == '!') { + // Not-combination, here we also accept one filter object or filter string + if (!is_array($filters) && is_a($filters, 'Net_LDAP_Filter')) { + $filters = array($filters); // force array + } elseif (is_string($filters)) { + $filter_o = Net_LDAP_Filter::parse($filters); + if (PEAR::isError($filter_o)) { + $err = PEAR::raiseError('Net_LDAP_Filter combine error: '.$filter_o->getMessage()); + return $err; + } else { + $filters = array($filter_o); + } + } else { + $err = PEAR::raiseError('Net_LDAP_Filter combine error: operator is "not" but $filter is not a valid Net_LDAP_Filter nor an array nor a filter string!'); + return $err; + } + } elseif ($log_op == '&' || $log_op == '|') { + if (!is_array($filters) || count($filters) < 2) { + $err = PEAR::raiseError('Net_LDAP_Filter combine error: parameter $filters is not an array or contains less than two Net_LDAP_Filter objects!'); + return $err; + } + } else { + $err = PEAR::raiseError('Net_LDAP_Filter combine error: logical operator is not known!'); + return $err; + } + + $combined_filter = new Net_LDAP_Filter(); + foreach ($filters as $key => $testfilter) { // check for errors + if (PEAR::isError($testfilter)) { + return $testfilter; + } elseif (is_string($testfilter)) { + // string found, try to parse into an filter object + $filter_o = Net_LDAP_Filter::parse($testfilter); + if (PEAR::isError($filter_o)) { + return $filter_o; + } else { + $filters[$key] = $filter_o; + } + } elseif (!is_a($testfilter, 'Net_LDAP_Filter')) { + $err = PEAR::raiseError('Net_LDAP_Filter combine error: invalid object passed in array $filters!'); + return $err; + } + } + + $combined_filter->_subfilters = $filters; + $combined_filter->_match = $log_op; + return $combined_filter; + } + + /** + * Parse FILTER into a Net_LDAP_Filter object + * + * This parses an filter string into Net_LDAP_Filter objects. + * + * @param string $FILTER The filter string + * + * @access static + * @return Net_LDAP_Filter|Net_LDAP_Error + * @todo Leaf-mode: Do we need to escape at all? what about *-chars?check for the need of encoding values, tackle problems (see code comments) + */ + function parse($FILTER) + { + if (preg_match('/^\((.+?)\)$/', $FILTER, $matches)) { + if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) { + // Subfilter processing: pass subfilters to parse() and combine + // the objects using the logical operator detected + // we have now something like "(...)(...)(...)" but at least one part ("(...)"). + + // extract logical operator and subfilters + $log_op = substr($matches[1], 0, 1); + $remaining_component = substr($matches[1], 1); + + // bite off the next filter part and parse + $subfilters = array(); + while (preg_match('/^(\(.+?\))(.*)/', $remaining_component, $matches)) { + $remaining_component = $matches[2]; + $filter_o = Net_LDAP_Filter::parse($matches[1]); + if (PEAR::isError($filter_o)) { + return $filter_o; + } + array_push($subfilters, $filter_o); + } + + // combine subfilters using the logical operator + $filter_o = Net_LDAP_Filter::combine($log_op, $subfilters); + return $filter_o; + } else { + // This is one leaf filter component, do some syntax checks, then escape and build filter_o + // $matches[1] should be now something like "foo=bar" + + // detect multiple leaf components + // [TODO] Maybe this will make problems with filters containing brackets inside the value + if (stristr($matches[1], ')(')) { + return PEAR::raiseError("Filter parsing error: invalid filter syntax - multiple leaf components detected!"); + } else { + $filter_parts = preg_split('/(?|<|>=|<=)/', $matches[1], 2, PREG_SPLIT_DELIM_CAPTURE); + if (count($filter_parts) != 3) { + return PEAR::raiseError("Filter parsing error: invalid filter syntax - unknown matching rule used"); + } else { + $filter_o = new Net_LDAP_Filter(); + // [TODO]: Do we need to escape at all? what about *-chars user provide and that should remain special? + // I think, those prevent escaping! We need to check against PERL Net::LDAP! + // $value_arr = Net_LDAP_Util::escape_filter_value(array($filter_parts[2])); + // $value = $value_arr[0]; + $value = $filter_parts[2]; + $filter_o->_filter = '('.$filter_parts[0].$filter_parts[1].$value.')'; + return $filter_o; + } + } + } + } else { + // ERROR: Filter components must be enclosed in round brackets + return PEAR::raiseError("Filter parsing error: invalid filter syntax - filter components must be enclosed in round brackets"); + } + } + + /** + * Get the string representation of this filter + * + * This method runs through all filter objects and creates + * the string representation of the filter. If this + * filter object is a leaf filter, then it will return + * the string representation of this filter. + * + * @return string|Net_LDAP_Error + */ + function asString() + { + if ($this->_isLeaf()) { + $return = $this->_filter; + } else { + $return = ''; + foreach ($this->_subfilters as $filter) { + $return = $return.$filter->asString(); + } + $return = '(' . $this->_match . $return . ')'; + } + return $return; + } + + /** + * Alias for perl interface as_string() + * + * @see asString() + */ + function as_string() + { + return $this->asString(); + } + + /** + * Print the text representation of the filter to FH, or the currently selected output handle if FH is not given + * + * This method is only for compatibility to the perl interface. + * However, the original method was called "print" but due to PHP language restrictions, + * we can't have a print() method. + * + * @param resource $FH (optional) A filehandle resource + * + * @return true|Net_LDAP_Error + */ + function printMe($FH = false) + { + if (!is_resource($FH)) { + if (PEAR::isError($FH)) { + return $FH; + } + $filter_str = $this->asString(); + if (PEAR::isError($filter_str)) { + return $filter_str; + } else { + print($filter_str); + } + } else { + $filter_str = $this->asString(); + if (PEAR::isError($filter_str)) { + return $filter_str; + } else { + $res = @fwrite($FH, $this->asString()); + if ($res == false) { + return PEAR::raiseError("Unable to write filter string to filehandle \$FH!"); + } + } + } + return true; + } + + /** + * This can be used to escape a string to provide a valid LDAP-Filter. + * + * LDAP will only recognise certain characters as the + * character istself if they are properly escaped. This is + * what this method does. + * The method can be called statically, so you can use it outside + * for your own purposes (eg for escaping only parts of strings) + * + * In fact, this is just a shorthand to {@link Net_LDAP_Util::escape_filter_value()}. + * For upward compatibiliy reasons you are strongly encouraged to use the escape + * methods provided by the Net_LDAP_Util class. + * + * @param string $value Any string who should be escaped + * + * @static + * @return string The string $string, but escaped + * @deprecated Do not use this method anymore, instead use Net_LDAP_Util::escape_filter_value() directly + */ + function escape($value) + { + $return = Net_LDAP_Util::escape_filter_value(array($value)); + return $return[0]; + } + + /** + * Is this a container or a leaf filter object? + * + * @access private + * @return boolean + */ + function _isLeaf() + { + if (count($this->_subfilters) > 0) { + return false; // Container! + } else { + return true; // Leaf! + } + } +} +?> diff --git a/thirdparty/pear/Net/LDAP/LDIF.php b/thirdparty/pear/Net/LDAP/LDIF.php new file mode 100644 index 0000000..4e176a4 --- /dev/null +++ b/thirdparty/pear/Net/LDAP/LDIF.php @@ -0,0 +1,925 @@ + +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: LDIF.php,v 1.33 2008/10/26 15:31:06 clockwerx Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ + +require_once 'PEAR.php'; +require_once 'Net/LDAP.php'; +require_once 'Net/LDAP/Entry.php'; +require_once 'Net/LDAP/Util.php'; + +/** +* LDIF capabilitys for Net_LDAP, closely taken from PERLs Net::LDAP +* +* It provides a means to convert between Net_LDAP_Entry objects and LDAP entries +* represented in LDIF format files. Reading and writing are supported and may +* manipulate single entries or lists of entries. +* +* Usage example: +* +* // Read and parse an ldif-file into Net_LDAP_Entry objects +* // and print out the DNs. Store the entries for later use. +* require 'Net/LDAP/LDIF.php'; +* $options = array( +* 'onerror' => 'die' +* ); +* $entries = array(); +* $ldif = new Net_LDAP_LDIF('test.ldif', 'r', $options); +* do { +* $entry = $ldif->read_entry(); +* $dn = $entry->dn(); +* echo " done building entry: $dn\n"; +* array_push($entries, $entry); +* } while (!$ldif->eof()); +* $ldif->done(); +* +* +* // write those entries to another file +* $ldif = new Net_LDAP_LDIF('test.out.ldif', 'w', $options); +* $ldif->write_entry($entries); +* $ldif->done(); +* +* +* @category Net +* @package Net_LDAP +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +* @see http://www.ietf.org/rfc/rfc2849.txt +* @todo Error handling should be PEARified +* @todo LDAPv3 controls are not implemented yet +*/ +class Net_LDAP_LDIF extends PEAR +{ + /** + * Options + * + * @access private + * @var array + */ + var $_options = array( + 'encode' => 'base64', + 'onerror' => 'undef', + 'change' => 0, + 'lowercase' => 0, + 'sort' => 0, + 'version' => 1, + 'wrap' => 78, + 'raw' => '' + ); + + /** + * Errorcache + * + * @access private + * @var array + */ + var $_error = array( + 'error' => null, + 'line' => 0 + ); + + /** + * Filehandle for read/write + * + * @access private + * @var array + */ + var $_FH = null; + + /** + * Says, if we opened the filehandle ourselves + * + * @access private + * @var array + */ + var $_FH_opened = false; + + /** + * Linecounter for input file handle + * + * @access private + * @var array + */ + var $_input_line = 0; + + /** + * counter for processed entries + * + * @access private + * @var int + */ + var $_entrynum = 0; + + /** + * Mode we are working in + * + * Either 'r', 'a' or 'w' + * + * @access private + * @var string + */ + var $_mode = false; + + /** + * Tells, if the LDIF version string was already written + * + * @access private + * @var boolean + */ + var $_version_written = false; + + /** + * Cache for lines that have build the current entry + * + * @access private + * @var boolean + */ + var $_lines_cur = array(); + + /** + * Cache for lines that will build the next entry + * + * @access private + * @var boolean + */ + var $_lines_next = array(); + + /** + * Open LDIF file for reading or for writing + * + * new (FILE): + * Open the file read-only. FILE may be the name of a file + * or an already open filehandle. + * If the file doesn't exist, it will be created if in write mode. + * + * new (FILE, MODE, OPTIONS): + * Open the file with the given MODE (see PHPs fopen()), eg "w" or "a". + * FILE may be the name of a file or an already open filehandle. + * PERLs Net_LDAP "FILE|" mode does not work curently. + * + * OPTIONS is an associative array and may contain: + * encode => 'none' | 'canonical' | 'base64' + * Some DN values in LDIF cannot be written verbatim and have to be encoded in some way: + * 'none' No encoding. + * 'canonical' See "canonical_dn()" in Net::LDAP::Util. + * 'base64' Use base64. (default, this differs from the Perl interface. + * The perl default is "none"!) + * + * onerror => 'die' | 'warn' | undef + * Specify what happens when an error is detected. + * 'die' Net_LDAP_LDIF will croak with an appropriate message. + * 'warn' Net_LDAP_LDIF will warn (echo) with an appropriate message. + * undef Net_LDAP_LDIF will not warn (default), use error(). + * + * change => 1 + * Write entry changes to the LDIF file instead of the entries itself. I.e. write LDAP + * operations acting on the entries to the file instead of the entries contents. + * This writes the changes usually carried out by an update() to the LDIF file. + * + * lowercase => 1 + * Convert attribute names to lowercase when writing. + * + * sort => 1 + * Sort attribute names when writing entries according to the rule: + * objectclass first then all other attributes alphabetically sorted by attribute name + * + * version => '1' + * Set the LDIF version to write to the resulting LDIF file. + * According to RFC 2849 currently the only legal value for this option is 1. + * When this option is set Net_LDAP_LDIF tries to adhere more strictly to + * the LDIF specification in RFC2489 in a few places. + * The default is undef meaning no version information is written to the LDIF file. + * + * wrap => 78 + * Number of columns where output line wrapping shall occur. + * Default is 78. Setting it to 40 or lower inhibits wrapping. + * + * [NOT IMPLEMENTED] raw => REGEX + * Use REGEX to denote the names of attributes that are to be + * considered binary in search results if writing entries. + * Example: raw => "/(?i:^jpegPhoto|;binary)/i" + * + * @param string|ressource $file Filename or filehandle + * @param string $mode Mode to open filename + * @param array $options Options like described above + */ + function Net_LDAP_LDIF($file, $mode = 'r', $options = array()) + { + $this->PEAR('Net_LDAP_Error'); // default error class + + // First, parse options + // todo: maybe implement further checks on possible values + foreach ($options as $option => $value) { + if (!array_key_exists($option, $this->_options)) { + $this->_dropError('Net_LDAP_LDIF error: option '.$option.' not known!'); + return; + } else { + $this->_options[$option] = strtolower($value); + } + } + + // setup LDIF class + $this->version($this->_options['version']); + + // setup file mode + if (!preg_match('/^[rwa]\+?$/', $mode)) { + $this->_dropError('Net_LDAP_LDIF error: file mode '.$mode.' not supported!'); + } else { + $this->_mode = $mode; + + // setup filehandle + if (is_resource($file)) { + // TODO: checks on mode possible? + $this->_FH =& $file; + } else { + $imode = substr($this->_mode, 0, 1); + if ($imode == 'r') { + if (!file_exists($file)) { + $this->_dropError('Unable to open '.$file.' for read: file not found'); + $this->_mode = false; + } + if (!is_readable($file)) { + $this->_dropError('Unable to open '.$file.' for read: permission denied'); + $this->_mode = false; + } + } + + if (($imode == 'w' || $imode == 'a')) { + if (file_exists($file)) { + if (!is_writable($file)) { + $this->_dropError('Unable to open '.$file.' for write: permission denied'); + $this->_mode = false; + } + } else { + if (!@touch($file)) { + $this->_dropError('Unable to create '.$file.' for write: permission denied'); + $this->_mode = false; + } + } + } + + if ($this->_mode) { + $this->_FH = @fopen($file, $this->_mode); + if (false === $this->_FH) { + // Fallback; should never be reached if tests above are good enough! + $this->_dropError('Net_LDAP_LDIF error: Could not open file '.$file); + } else { + $this->_FH_opened = true; + } + } + } + } + } + + /** + * Read one entry from the file and return it as a Net::LDAP::Entry object. + * + * @return Net_LDAP_Entry + */ + function read_entry() + { + // read fresh lines, set them as current lines and create the entry + $attrs = $this->next_lines(true); + if (count($attrs) > 0) { + $this->_lines_cur = $attrs; + } + return $this->current_entry(); + } + + /** + * Returns true when the end of the file is reached. + * + * @return boolean + */ + function eof() + { + return feof($this->_FH); + } + + /** + * Write the entry or entries to the LDIF file. + * + * If you want to build an LDIF file containing several entries AND + * you want to call write_entry() several times, you must open the filehandle + * in append mode ("a"), otherwise you will always get the last entry only. + * + * @param Net_LDAP_Entry|array $entries Entry or array of entries + * + * @return void + * @todo implement operations on whole entries (adding a whole entry) + */ + function write_entry($entries) + { + if (!is_array($entries)) { + $entries = array($entries); + } + + foreach ($entries as $entry) { + $this->_entrynum++; + if (!is_a($entry, 'Net_LDAP_Entry')) { + $this->_dropError('Net_LDAP_LDIF error: entry '.$this->_entrynum.' is not an Net_LDAP_Entry object'); + } else { + if ($this->_options['change']) { + // LDIF change mode + // fetch change information from entry + $entry_attrs_changes = $entry->getChanges(); + $num_of_changes = count($entry_attrs_changes['add']) + + count($entry_attrs_changes['replace']) + + count($entry_attrs_changes['delete']); + + $is_changed = ($num_of_changes > 0 || $entry->willBeDeleted() || $entry->willBeMoved()); + + // write version if not done yet + // also write DN of entry + if ($is_changed) { + if (!$this->_version_written) { + $this->write_version(); + } + $this->_writeDN($entry->currentDN()); + } + + // process changes + // TODO: consider DN add! + if ($entry->willBeDeleted()) { + $this->_writeLine("changetype: delete".PHP_EOL); + } elseif ($entry->willBeMoved()) { + $this->_writeLine("changetype: modrdn".PHP_EOL); + $olddn = Net_LDAP_Util::ldap_explode_dn($entry->currentDN(), array('casefold' => 'none')); // maybe gives a bug if using multivalued RDNs + $oldrdn = array_shift($olddn); + $oldparent = implode(',', $olddn); + $newdn = Net_LDAP_Util::ldap_explode_dn($entry->dn(), array('casefold' => 'none')); // maybe gives a bug if using multivalued RDNs + $rdn = array_shift($newdn); + $parent = implode(',', $newdn); + $this->_writeLine("newrdn: ".$rdn.PHP_EOL); + $this->_writeLine("deleteoldrdn: 1".PHP_EOL); + if ($parent !== $oldparent) { + $this->_writeLine("newsuperior: ".$parent.PHP_EOL); + } + // TODO: What if the entry has attribute changes as well? + // I think we should check for that and make a dummy + // entry with the changes that is written to the LDIF file + } elseif ($num_of_changes > 0) { + // write attribute change data + $this->_writeLine("changetype: modify".PHP_EOL); + foreach ($entry_attrs_changes as $changetype => $entry_attrs) { + foreach ($entry_attrs as $attr_name => $attr_values) { + $this->_writeLine("$changetype: $attr_name".PHP_EOL); + if ($attr_values !== null) { + $this->_writeAttribute($attr_name, $attr_values, $changetype); + } + $this->_writeLine("-".PHP_EOL); + } + } + } + + // finish this entrys data if we had changes + if ($is_changed) { + $this->_finishEntry(); + } + } else { + // LDIF-content mode + // fetch attributes for further processing + $entry_attrs = $entry->getValues(); + + // sort and put objectclass-attrs to first position + if ($this->_options['sort']) { + ksort($entry_attrs); + if (array_key_exists('objectclass', $entry_attrs)) { + $oc = $entry_attrs['objectclass']; + unset($entry_attrs['objectclass']); + $entry_attrs = array_merge(array('objectclass' => $oc), $entry_attrs); + } + } + + // write data + if (!$this->_version_written) { + $this->write_version(); + } + $this->_writeDN($entry->dn()); + foreach ($entry_attrs as $attr_name => $attr_values) { + $this->_writeAttribute($attr_name, $attr_values); + } + $this->_finishEntry(); + } + } + } + } + + /** + * Write version to LDIF + * + * If the object's version is defined, this method allows to explicitely write the version before an entry is written. + * If not called explicitely, it gets called automatically when writing the first entry. + * + * @return void + */ + function write_version() + { + $this->_version_written = true; + return $this->_writeLine('version: '.$this->version().PHP_EOL, 'Net_LDAP_LDIF error: unable to write version'); + } + + /** + * Get or set LDIF version + * + * If called without arguments it returns the version of the LDIF file or undef if no version has been set. + * If called with an argument it sets the LDIF version to VERSION. + * According to RFC 2849 currently the only legal value for VERSION is 1. + * + * @param int $version (optional) LDIF version to set + * + * @return int + */ + function version($version = null) + { + if ($version !== null) { + if ($version != 1) { + $this->_dropError('Net_LDAP_LDIF error: illegal LDIF version set'); + } else { + $this->_version = $version; + } + } + return $this->_version; + } + + /** + * Returns the file handle the Net_LDAP_LDIF object reads from or writes to. + * + * You can, for example, use this to fetch the content of the LDIF file yourself + * + * @return null|resource + */ + function &handle() + { + if (!is_resource($this->_FH)) { + $this->_dropError('Net_LDAP_LDIF error: invalid file resource'); + $null = null; + return $null; + } else { + return $this->_FH; + } + } + + /** + * Clean up + * + * This method signals that the LDIF object is no longer needed. + * You can use this to free up some memory and close the file handle. + * The file handle is only closed, if it was opened from Net_LDAP_LDIF. + * + * @return void + */ + function done() + { + // close FH if we opened it + if ($this->_FH_opened) { + fclose($this->handle()); + } + + // free variables + foreach (get_object_vars($this) as $name => $value) { + unset($this->$name); + } + } + + /** + * Returns last error message if error was found. + * + * Example: + * + * $ldif->someAction(); + * if ($ldif->error()) { + * echo "Error: ".$ldif->error()." at input line: ".$ldif->error_lines(); + * } + * + * + * @param boolean $as_string If set to true, only the message is returned + * + * @return false|Net_LDAP_Error + */ + function error($as_string = false) + { + if (Net_LDAP::isError($this->_error['error'])) { + return ($as_string)? $this->_error['error']->getMessage() : $this->_error['error']; + } else { + return false; + } + } + + /** + * Returns lines that resulted in error. + * + * Perl returns an array of faulty lines in list context, + * but we always just return an int because of PHPs language. + * + * @return int + */ + function error_lines() + { + return $this->_error['line']; + } + + /** + * Returns the current Net::LDAP::Entry object. + * + * @return Net_LDAP_Entry|false + */ + function current_entry() + { + return $this->parseLines($this->current_lines()); + } + + /** + * Parse LDIF lines of one entry into an Net_LDAP_Entry object + * + * @param array $lines LDIF lines for one entry + * + * @return Net_LDAP_Entry|false Net_LDAP_Entry object for those lines + * @todo what about file inclusions and urls? "jpegphoto:< file:///usr/local/directory/photos/fiona.jpg" + */ + function parseLines($lines) + { + // parse lines into an array of attributes and build the entry + $attributes = array(); + + $dn = false; + foreach ($lines as $line) { + if (preg_match('/^(\w+)(:|::|:<)\s(.+)$/', $line, $matches)) { + $attr =& $matches[1]; + $delim =& $matches[2]; + $data =& $matches[3]; + + if ($delim == ':') { + // normal data + $attributes[$attr][] = $data; + } elseif ($delim == '::') { + // base64 data + $attributes[$attr][] = base64_decode($data); + } elseif ($delim == ':<') { + // file inclusion + // TODO: Is this the job of the LDAP-client or the server? + $this->_dropError('File inclusions are currently not supported'); + //$attributes[$attr][] = ...; + } else { + // since the pattern above, the delimeter cannot be something else. + $this->_dropError('Net_LDAP_LDIF parsing error: invalid syntax at parsing entry line: '.$line); + continue; + } + + if (strtolower($attr) == 'dn') { + // DN line detected + $dn = $attributes[$attr][0]; // save possibly decoded DN + unset($attributes[$attr]); // remove wrongly added "dn: " attribute + } + } else { + // line not in "attr: value" format -> ignore + // maybe we should rise an error here, but this should be covered by + // next_lines() already. A problem arises, if users try to feed data of + // several entries to this method - the resulting entry will + // get wrong attributes. However, this is already mentioned in the + // methods documentation above. + } + } + + if (false === $dn) { + $this->_dropError('Net_LDAP_LDIF parsing error: unable to detect DN for entry'); + return false; + } else { + $newentry = Net_LDAP_Entry::createFresh($dn, $attributes); + return $newentry; + } + } + + /** + * Returns the lines that generated the current Net::LDAP::Entry object. + * + * Note that this returns an empty array if no lines have been read so far. + * + * @return array Array of lines + */ + function current_lines() + { + return $this->_lines_cur; + } + + /** + * Returns the lines that will generate the next Net::LDAP::Entry object. + * + * If you set $force to TRUE then you can iterate over the lines that build + * up entries manually. Otherwise, iterating is done using {@link read_entry()}. + * Force will move the file pointer forward, thus returning the next entries lines. + * + * Wrapped lines will be unwrapped. Comments are stripped. + * + * @param boolean $force Set this to true if you want to iterate over the lines manually + * + * @return array + */ + function next_lines($force = false) + { + // if we already have those lines, just return them, otherwise read + if (count($this->_lines_next) == 0 || $force) { + $this->_lines_next = array(); // empty in case something was left (if used $force) + $entry_done = false; + $fh = &$this->handle(); + $commentmode = false; // if we are in an comment, for wrapping purposes + $datalines_read = 0; // how many lines with data we have read + + while (!$entry_done && !$this->eof()) { + $this->_input_line++; + // Read line. Remove line endings, we want only data; + // this is okay since ending spaces should be encoded + $data = rtrim(fgets($fh)); + if ($data === false) { + // error only, if EOF not reached after fgets() call + if (!$this->eof()) { + $this->_dropError('Net_LDAP_LDIF error: error reading from file at input line '.$this->_input_line, $this->_input_line); + } + break; + } else { + if (count($this->_lines_next) > 0 && preg_match('/^$/', $data)) { + // Entry is finished if we have an empty line after we had data + $entry_done = true; + + // Look ahead if the next EOF is nearby. Comments and empty + // lines at the file end may cause problems otherwise + $current_pos = ftell($fh); + $data = fgets($fh); + while (!feof($fh)) { + if (preg_match('/^\s*$/', $data) || preg_match('/^#/', $data)) { + // only empty lines or comments, continue to seek + // TODO: Known bug: Wrappings for comments are okay but are treaten as + // error, since we do not honor comment mode here. + // This should be a very theoretically case, however + // i am willing to fix this if really necessary. + $this->_input_line++; + $current_pos = ftell($fh); + $data = fgets($fh); + } else { + // Data found if non emtpy line and not a comment!! + // Rewind to position prior last read and stop lookahead + fseek($fh, $current_pos); + break; + } + } + // now we have either the file pointer at the beginning of + // a new data position or at the end of file causing feof() to return true + + } else { + // build lines + if (preg_match('/^version:\s(.+)$/', $data, $match)) { + // version statement, set version + $this->version($match[1]); + } elseif (preg_match('/^\w+::?\s.+$/', $data)) { + // normal attribute: add line + $commentmode = false; + $this->_lines_next[] = trim($data); + $datalines_read++; + } elseif (preg_match('/^\s(.+)$/', $data, $matches)) { + // wrapped data: unwrap if not in comment mode + if (!$commentmode) { + if ($datalines_read == 0) { + // first line of entry: wrapped data is illegal + $this->_dropError('Net_LDAP_LDIF error: illegal wrapping at input line '.$this->_input_line, $this->_input_line); + } else { + $last = array_pop($this->_lines_next); + $last = $last.trim($matches[1]); + $this->_lines_next[] = $last; + $datalines_read++; + } + } + } elseif (preg_match('/^#/', $data)) { + // LDIF comments + $commentmode = true; + } elseif (preg_match('/^\s*$/', $data)) { + // empty line but we had no data for this + // entry, so just ignore this line + $commentmode = false; + } else { + $this->_dropError('Net_LDAP_LDIF error: invalid syntax at input line '.$this->_input_line, $this->_input_line); + continue; + } + + } + } + } + } + return $this->_lines_next; + } + + /** + * Convert an attribute and value to LDIF string representation + * + * It honors correct encoding of values according to RFC 2849. + * Line wrapping will occur at the configured maximum but only if + * the value is greater than 40 chars. + * + * @param string $attr_name Name of the attribute + * @param string $attr_value Value of the attribute + * + * @access private + * @return string LDIF string for that attribute and value + */ + function _convertAttribute($attr_name, $attr_value) + { + // Handle empty attribute or process + if (strlen($attr_value) == 0) { + $attr_value = " "; + } else { + $base64 = false; + // ASCII-chars that are NOT safe for the + // start and for being inside the value. + // These are the int values of those chars. + $unsafe_init = array(0, 10, 13, 32, 58, 60); + $unsafe = array(0, 10, 13); + + // Test for illegal init char + $init_ord = ord(substr($attr_value, 0, 1)); + if ($init_ord >= 127 || in_array($init_ord, $unsafe_init)) { + $base64 = true; + } + + // Test for illegal content char + for ($i = 0; $i < strlen($attr_value); $i++) { + $char = substr($attr_value, $i, 1); + if (ord($char) >= 127 || in_array($init_ord, $unsafe)) { + $base64 = true; + } + } + + // Test for ending space + if (substr($attr_value, -1) == ' ') { + $base64 = true; + } + + // If converting is needed, do it + if ($base64 && !($this->_options['raw'] && preg_match($this->_options['raw'], $attr_name))) { + $attr_name .= ':'; + $attr_value = base64_encode($attr_value); + } + + // Lowercase attr names if requested + if ($this->_options['lowercase']) { + $attr_name = strtolower($attr_name); + } + + // Handle line wrapping + if ($this->_options['wrap'] > 40 && strlen($attr_value) > $this->_options['wrap']) { + $attr_value = wordwrap($attr_value, $this->_options['wrap'], PHP_EOL." ", true); + } + } + + return $attr_name.': '.$attr_value; + } + + /** + * Convert an entries DN to LDIF string representation + * + * It honors correct encoding of values according to RFC 2849. + * + * @param string $dn UTF8-Encoded DN + * + * @access private + * @return string LDIF string for that DN + * @todo I am not sure, if the UTF8 stuff is correctly handled right now + */ + function _convertDN($dn) + { + $base64 = false; + // ASCII-chars that are NOT safe for the + // start and for being inside the dn. + // These are the int values of those chars. + $unsafe_init = array(0, 10, 13, 32, 58, 60); + $unsafe = array(0, 10, 13); + + // Test for illegal init char + $init_ord = ord(substr($dn, 0, 1)); + if ($init_ord >= 127 || in_array($init_ord, $unsafe_init)) { + $base64 = true; + } + + // Test for illegal content char + for ($i = 0; $i < strlen($dn); $i++) { + $char = substr($dn, $i, 1); + if (ord($char) >= 127 || in_array($init_ord, $unsafe)) { + $base64 = true; + } + } + + // Test for ending space + if (substr($dn, -1) == ' ') { + $base64 = true; + } + + // if converting is needed, do it + return ($base64)? 'dn:: '.base64_encode($dn) : 'dn: '.$dn; + } + + /** + * Writes an attribute to the filehandle + * + * @param string $attr_name Name of the attribute + * @param string|array $attr_values Single attribute value or array with attribute values + * + * @access private + * @return void + */ + function _writeAttribute($attr_name, $attr_values) + { + // write out attribute content + if (!is_array($attr_values)) { + $attr_values = array($attr_values); + } + foreach ($attr_values as $attr_val) { + $line = $this->_convertAttribute($attr_name, $attr_val).PHP_EOL; + $this->_writeLine($line, 'Net_LDAP_LDIF error: unable to write attribute '.$attr_name.' of entry '.$this->_entrynum); + } + } + + /** + * Writes a DN to the filehandle + * + * @param string $dn DN to write + * + * @access private + * @return void + */ + function _writeDN($dn) + { + // prepare DN + if ($this->_options['encode'] == 'base64') { + $dn = $this->_convertDN($dn).PHP_EOL; + } elseif ($this->_options['encode'] == 'canonical') { + $dn = Net_LDAP_Util::canonical_dn($dn, array('casefold' => 'none')).PHP_EOL; + } else { + $dn = $dn.PHP_EOL; + } + $this->_writeLine($dn, 'Net_LDAP_LDIF error: unable to write DN of entry '.$this->_entrynum); + } + + /** + * Finishes an LDIF entry + * + * @access private + * @return void + */ + function _finishEntry() + { + $this->_writeLine(PHP_EOL, 'Net_LDAP_LDIF error: unable to close entry '.$this->_entrynum); + } + + /** + * Just write an arbitary line to the filehandle + * + * @param string $line Content to write + * @param string $error If error occurs, drop this message + * + * @access private + * @return true|false + */ + function _writeLine($line, $error = 'Net_LDAP_LDIF error: unable to write to filehandle') + { + if (is_resource($this->handle()) && fwrite($this->handle(), $line, strlen($line)) === false) { + $this->_dropError($error); + return false; + } else { + return true; + } + } + + /** + * Optionally raises an error and pushes the error on the error cache + * + * @param string $msg Errortext + * @param int $line Line in the LDIF that caused the error + * + * @access private + * @return void + */ + function _dropError($msg, $line = null) + { + $this->_error['error'] = new Net_LDAP_Error($msg); + if ($line !== null) { + $this->_error['line'] = $line; + } + + if ($this->_options['onerror'] == 'die') { + die($msg.PHP_EOL); + } elseif ($this->_options['onerror'] == 'warn') { + echo $msg.PHP_EOL; + } + } +} +?> diff --git a/thirdparty/pear/Net/LDAP/RootDSE.php b/thirdparty/pear/Net/LDAP/RootDSE.php old mode 100644 new mode 100755 index 54dee47..16c9527 --- a/thirdparty/pear/Net/LDAP/RootDSE.php +++ b/thirdparty/pear/Net/LDAP/RootDSE.php @@ -1,192 +1,194 @@ +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: RootDSE.php,v 1.12 2008/10/26 15:31:06 clockwerx Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ +require_once 'PEAR.php'; /** - * Getting the rootDSE entry of a LDAP server - * - * @package Net_LDAP - * @author Jan Wagner - * @version $Revision$ - */ +* Getting the rootDSE entry of a LDAP server +* +* @category Net +* @package Net_LDAP +* @author Jan Wagner +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ class Net_LDAP_RootDSE extends PEAR { /** - * @access private - * @var object Net_LDAP_Entry - **/ + * @access private + * @var object Net_LDAP_Entry + **/ var $_entry; - + /** - * class constructor - * - * @param object Net_LDAP_Entry - */ + * Class constructor + * + * @param Net_LDAP_Entry &$entry Net_LDAP_Entry object + */ function Net_LDAP_RootDSE(&$entry) { - $this->_entry = $entry; + $this->_entry = $entry; } /** - * Gets the requested attribute value - * - * Same usuage as Net_LDAP_Entry::get_value() - * - * @access public - * @param string Attribute name - * @param array Array of options - * @return mixed Net_LDAP_Error object or attribute values - * @see Net_LDAP_Entry::get_value() - */ + * Gets the requested attribute value + * + * Same usuage as {@link Net_LDAP_Entry::getValue()} + * + * @param string $attr Attribute name + * @param array $options Array of options + * + * @access public + * @return mixed Net_LDAP_Error object or attribute values + * @see Net_LDAP_Entry::get_value() + */ function getValue($attr = '', $options = '') { return $this->_entry->get_value($attr, $options); } /** - * alias function of getValue() for perl-ldap interface - * - * @see getValue() - */ - function get_value() - { + * Alias function of getValue() for perl-ldap interface + * + * @see getValue() + */ + function get_value() + { $args = func_get_args(); - return call_user_func_array(array($this, 'getValue' ), $args); - } - + return call_user_func_array(array( &$this, 'getValue' ), $args); + } + /** - * Determines if the extension is supported - * - * @access public - * @param array Array of oids to check - * @return boolean - */ - function supportedExtension($oids) - { + * Determines if the extension is supported + * + * @param array $oids Array of oids to check + * + * @access public + * @return boolean + */ + function supportedExtension($oids) + { return $this->_checkAttr($oids, 'supportedExtension'); } - + /** - * alias function of supportedExtension() for perl-ldap interface - * - * @see supportedExtension() - */ - function supported_extension() - { + * Alias function of supportedExtension() for perl-ldap interface + * + * @see supportedExtension() + */ + function supported_extension() + { $args = func_get_args(); - return call_user_func_array(array($this, 'supportedExtension'), $args); - } - + return call_user_func_array(array( &$this, 'supportedExtension'), $args); + } + /** - * Determines if the version is supported - * - * @access public - * @param array Versions to check - * @return boolean - */ - function supportedVersion($versions) + * Determines if the version is supported + * + * @param array $versions Versions to check + * + * @access public + * @return boolean + */ + function supportedVersion($versions) { return $this->_checkAttr($versions, 'supportedLDAPVersion'); } /** - * alias function of supportedVersion() for perl-ldap interface - * - * @see supportedVersion() - */ - function supported_version() - { + * Alias function of supportedVersion() for perl-ldap interface + * + * @see supportedVersion() + */ + function supported_version() + { $args = func_get_args(); - return call_user_func_array(array($this, 'supportedVersion'), $args); - } + return call_user_func_array(array(&$this, 'supportedVersion'), $args); + } - /** - * Determines if the control is supported - * - * @access public - * @param array Control oids to check - * @return boolean - */ + /** + * Determines if the control is supported + * + * @param array $oids Control oids to check + * + * @access public + * @return boolean + */ function supportedControl($oids) { return $this->_checkAttr($oids, 'supportedControl'); } - + /** - * alias function of supportedControl() for perl-ldap interface - * - * @see supportedControl() - */ - function supported_control() - { + * Alias function of supportedControl() for perl-ldap interface + * + * @see supportedControl() + */ + function supported_control() + { $args = func_get_args(); - return call_user_func_array(array($this, 'supportedControl' ), $args); - } - + return call_user_func_array(array(&$this, 'supportedControl' ), $args); + } + /** - * Determines if the sasl mechanism is supported - * - * @access public - * @param array SASL mechanisms to check - * @return boolean - */ + * Determines if the sasl mechanism is supported + * + * @param array $mechlist SASL mechanisms to check + * + * @access public + * @return boolean + */ function supportedSASLMechanism($mechlist) { return $this->_checkAttr($mechlist, 'supportedSASLMechanisms'); } /** - * alias function of supportedSASLMechanism() for perl-ldap interface - * - * @see supportedSASLMechanism() - */ - function supported_sasl_mechanism() - { + * Alias function of supportedSASLMechanism() for perl-ldap interface + * + * @see supportedSASLMechanism() + */ + function supported_sasl_mechanism() + { $args = func_get_args(); - return call_user_func_array(array($this, 'supportedSASLMechanism'), $args); - } - - /** - * Checks for existance of value in attribute - * - * @access private - * @param array $values values to check - * @param attr $attr attribute name - * @return boolean - */ + return call_user_func_array(array(&$this, 'supportedSASLMechanism'), $args); + } + + /** + * Checks for existance of value in attribute + * + * @param array $values values to check + * @param string $attr attribute name + * + * @access private + * @return boolean + */ function _checkAttr($values, $attr) { if (!is_array($values)) $values = array($values); - + foreach ($values as $value) { - if (!@in_array($value, $this->get_value($attr))) { + if (!@in_array($value, $this->get_value($attr, 'all'))) { return false; } - } - return true; + } + return true; } } -?> \ No newline at end of file +?> diff --git a/thirdparty/pear/Net/LDAP/Schema.php b/thirdparty/pear/Net/LDAP/Schema.php old mode 100644 new mode 100755 index 75e0322..201a81d --- a/thirdparty/pear/Net/LDAP/Schema.php +++ b/thirdparty/pear/Net/LDAP/Schema.php @@ -1,49 +1,61 @@ +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: Schema.php,v 1.23 2008/10/26 15:31:06 clockwerx Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ +require_once 'PEAR.php'; + +/** +* Syntax definitions +* +* Please don't forget to add binary attributes to isBinary() below +* to support proper value fetching from Net_LDAP_Entry +*/ +define('NET_LDAP_SYNTAX_BOOLEAN', '1.3.6.1.4.1.1466.115.121.1.7'); +define('NET_LDAP_SYNTAX_DIRECTORY_STRING', '1.3.6.1.4.1.1466.115.121.1.15'); +define('NET_LDAP_SYNTAX_DISTINGUISHED_NAME', '1.3.6.1.4.1.1466.115.121.1.12'); +define('NET_LDAP_SYNTAX_INTEGER', '1.3.6.1.4.1.1466.115.121.1.27'); +define('NET_LDAP_SYNTAX_JPEG', '1.3.6.1.4.1.1466.115.121.1.28'); +define('NET_LDAP_SYNTAX_NUMERIC_STRING', '1.3.6.1.4.1.1466.115.121.1.36'); +define('NET_LDAP_SYNTAX_OID', '1.3.6.1.4.1.1466.115.121.1.38'); +define('NET_LDAP_SYNTAX_OCTET_STRING', '1.3.6.1.4.1.1466.115.121.1.40'); /** - * Load an LDAP Schema and provide information - * - * This class takes a Subschema entry, parses this information - * and makes it available in an array. Most of the code has been - * inspired by perl-ldap( http://perl-ldap.sourceforge.net). - * You will find portions of their implementation in here. - * - * @package Net_LDAP - * @author Jan Wagner - * @version $Revision$ - */ - class Net_LDAP_Schema extends PEAR - { +* Load an LDAP Schema and provide information +* +* This class takes a Subschema entry, parses this information +* and makes it available in an array. Most of the code has been +* inspired by perl-ldap( http://perl-ldap.sourceforge.net). +* You will find portions of their implementation in here. +* +* @category Net +* @package Net_LDAP +* @author Jan Wagner +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ +class Net_LDAP_Schema extends PEAR +{ /** - * Map of entry types to ldap attributes of subschema entry - * - * @access public - * @var array - */ + * Map of entry types to ldap attributes of subschema entry + * + * @access public + * @var array + */ var $types = array('attribute' => 'attributeTypes', 'ditcontentrule' => 'dITContentRules', 'ditstructurerule' => 'dITStructureRules', @@ -53,12 +65,12 @@ 'objectclass' => 'objectClasses', 'syntax' => 'ldapSyntaxes'); - /**#@+ - * Array of entries belonging to this type - * - * @access private - * @var array - */ + /** + * Array of entries belonging to this type + * + * @access private + * @var array + */ var $_attributeTypes = array(); var $_matchingRules = array(); var $_matchingRuleUse = array(); @@ -67,37 +79,48 @@ var $_dITContentRules = array(); var $_dITStructureRules = array(); var $_nameForms = array(); - /**#@-*/ + /** - * hash of all fetched oids - * - * @access private - * @var array - */ + * hash of all fetched oids + * + * @access private + * @var array + */ var $_oids = array(); - + /** - * constructor of the class - * - * @access protected - */ + * Tells if the schema is initialized + * + * @access private + * @var boolean + * @see parse(), get() + */ + var $_initialized = false; + + + /** + * constructor of the class + * + * @access protected + */ function Net_LDAP_Schema() { $this->PEAR('Net_LDAP_Error'); // default error class } /** - * Return a hash of entries for the given type - * - * Returns a hash of entry for th givene type. Types may be: - * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules, - * matchingruleuses, nameforms, syntaxes - * - * @access public - * @param string Type to fetch - * @return mixed Array or Net_LDAP_Error - */ + * Return a hash of entries for the given type + * + * Returns a hash of entry for th givene type. Types may be: + * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules, + * matchingruleuses, nameforms, syntaxes + * + * @param string $type Type to fetch + * + * @access public + * @return array|Net_LDAP_Error Array or Net_LDAP_Error + */ function &getAll($type) { $map = array('objectclasses' => &$this->_objectClasses, @@ -110,92 +133,102 @@ 'syntaxes' => &$this->_ldapSyntaxes ); $key = strtolower($type); - return ((key_exists($key, $map)) ? $map[$key] : $this->raiseError("Unknown type $type")); + $ret = ((key_exists($key, $map)) ? $map[$key] : PEAR::raiseError("Unknown type $type")); + return $ret; } - + /** - * Return a specific entry - * - * @access public - * @param string Type of name - * @param string Name or OID to fetch - * @return mixed Entry or Net_LDAP_Error - */ - function &get($type, $name) - { - $type = strtolower($type); - if (false == key_exists($type, $this->types)) { - return $this->raiseError("No such type $type"); - } + * Return a specific entry + * + * @param string $type Type of name + * @param string $name Name or OID to fetch + * + * @access public + * @return mixed Entry or Net_LDAP_Error + */ + function &get($type, $name) + { + if ($this->_initialized) { + $type = strtolower($type); + if (false == key_exists($type, $this->types)) { + return PEAR::raiseError("No such type $type"); + } - $name = strtolower($name); - $type_var = &$this->{'_' . $this->types[$type]}; - - if( key_exists($name, $type_var)) { - return $type_var[$name]; - } elseif(key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) { - return $this->_oids[$name]; + $name = strtolower($name); + $type_var = &$this->{'_' . $this->types[$type]}; + + if (key_exists($name, $type_var)) { + return $type_var[$name]; + } elseif (key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) { + return $this->_oids[$name]; + } else { + return PEAR::raiseError("Could not find $type $name"); + } } else { - return $this->raiseError("Could not find $type $name"); + $return = null; + return $return; } - } + } + - /** - * Fetches attributes that MAY be present in the given objectclass - * - * @access public - * @param string Name or OID of objectclass - * @return mixed Array with attributes or Net_LDAP_Error - */ + * Fetches attributes that MAY be present in the given objectclass + * + * @param string $oc Name or OID of objectclass + * + * @access public + * @return array|Net_LDAP_Error Array with attributes or Net_LDAP_Error + */ function may($oc) { return $this->_getAttr($oc, 'may'); } - + /** - * Fetches attributes that MUST be present in the given objectclass - * - * @access public - * @param string Name or OID of objectclass - * @return mixed Array with attributes or Net_LDAP_Error - */ + * Fetches attributes that MUST be present in the given objectclass + * + * @param string $oc Name or OID of objectclass + * + * @access public + * @return array|Net_LDAP_Error Array with attributes or Net_LDAP_Error + */ function must($oc) { return $this->_getAttr($oc, 'must'); } - + /** - * Fetches the given attribute from the given objectclass - * - * @access private - * @param string Name or OID of objectclass - * @param string Name of attribute to fetch - * @return mixed The attribute or Net_LDAP_Error - */ + * Fetches the given attribute from the given objectclass + * + * @param string $oc Name or OID of objectclass + * @param string $attr Name of attribute to fetch + * + * @access private + * @return array|Net_LDAP_Error The attribute or Net_LDAP_Error + */ function _getAttr($oc, $attr) { $oc = strtolower($oc); if (key_exists($oc, $this->_objectClasses) && key_exists($attr, $this->_objectClasses[$oc])) { return $this->_objectClasses[$oc][$attr]; - } - elseif (key_exists($oc, $this->_oids) && + } elseif (key_exists($oc, $this->_oids) && $this->_oids[$oc]['type'] == 'objectclass' && key_exists($attr, $this->_oids[$oc])) { return $this->_oids[$oc][$attr]; } else { - return $this->raiseError("Could not find $attr attributes for $oc "); + return PEAR::raiseError("Could not find $attr attributes for $oc "); } } - + /** - * Returns the name(s) of the immediate superclass(es) - * - * @param string Name or OID of objectclass - * @return mixed Array of names or Net_LDAP_Error - */ + * Returns the name(s) of the immediate superclass(es) + * + * @param string $oc Name or OID of objectclass + * + * @return array|Net_LDAP_Error Array of names or Net_LDAP_Error + */ function superclass($oc) - { + { $o = $this->get('objectclass', $oc); if (Net_LDAP::isError($o)) { return $o; @@ -204,55 +237,57 @@ } /** - * Parses the schema of the given Subschema entry - * - * @access public - * @param object Net_LDAP_Entry Subschema entry - */ + * Parses the schema of the given Subschema entry + * + * @param Net_LDAP_Entry &$entry Subschema entry + * + * @access public + */ function parse(&$entry) { - foreach ($this->types as $type => $attr) - { + foreach ($this->types as $type => $attr) { // initialize map type to entry - $type_var = '_' . $attr; + $type_var = '_' . $attr; $this->{$type_var} = array(); - + // get values for this type - $values = $entry->get_value($attr); - - if (is_array($values)) - { - foreach ($values as $value) { - - unset($schema_entry); // this was a real mess without it - - // get the schema entry - $schema_entry = $this->_parse_entry($value); - - // set the type - $schema_entry['type'] = $type; - - // save a ref in $_oids - $this->_oids[$schema_entry['oid']] =& $schema_entry; - - // save refs for all names in type map - $names = $schema_entry['aliases']; - array_push($names, $schema_entry['name']); - foreach ($names as $name) { - $this->{$type_var}[strtolower($name)] =& $schema_entry; + if ($entry->exists($attr)) { + $values = $entry->getValue($attr); + if (is_array($values)) { + foreach ($values as $value) { + + unset($schema_entry); // this was a real mess without it + + // get the schema entry + $schema_entry = $this->_parse_entry($value); + + // set the type + $schema_entry['type'] = $type; + + // save a ref in $_oids + $this->_oids[$schema_entry['oid']] = &$schema_entry; + + // save refs for all names in type map + $names = $schema_entry['aliases']; + array_push($names, $schema_entry['name']); + foreach ($names as $name) { + $this->{$type_var}[strtolower($name)] = &$schema_entry; + } } } } } + $this->_initialized = true; } - + /** - * parses an attribute value into a schema entry - * - * @access private - * @param string Attribute value - * @return mixed Schema entry array or false - */ + * parses an attribute value into a schema entry + * + * @param string $value Attribute value + * + * @access private + * @return array|false Schema entry array or false + */ function &_parse_entry($value) { // tokens that have no value associated @@ -263,20 +298,20 @@ 'abstract', 'structural', 'auxiliary'); - + // tokens that can have multiple values $multiValue = array('must', 'may', 'sup'); - + $schema_entry = array('aliases' => array()); // initilization - + $tokens = $this->_tokenize($value); // get an array of tokens - + // remove surrounding brackets if ($tokens[0] == '(') array_shift($tokens); if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-( $schema_entry['oid'] = array_shift($tokens); // first token is the oid - + // cycle over the tokens until none are left while (count($tokens) > 0) { $token = strtolower(array_shift($tokens)); @@ -294,12 +329,12 @@ } } // create a array if the value should be multivalued but was not - if (in_array($token, $multiValue ) && !is_array($schema_entry[$token])) { + if (in_array($token, $multiValue) && !is_array($schema_entry[$token])) { $schema_entry[$token] = array($schema_entry[$token]); } } - } - // get max length from syntax + } + // get max length from syntax if (key_exists('syntax', $schema_entry)) { if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) { $schema_entry['max_length'] = $matches[1]; @@ -308,31 +343,32 @@ // force a name if (empty($schema_entry['name'])) { $schema_entry['name'] = $schema_entry['oid']; - } + } // make one name the default and put the other ones into aliases if (is_array($schema_entry['name'])) { - $aliases = $schema_entry['name']; - $schema_entry['name'] = array_shift($aliases); + $aliases = $schema_entry['name']; + $schema_entry['name'] = array_shift($aliases); $schema_entry['aliases'] = $aliases; - } + } return $schema_entry; } - + /** - * tokenizes the given value into an array of tokens - * - * @access private - * @param string String to parse - * @return array Array of tokens - */ + * tokenizes the given value into an array of tokens + * + * @param string $value String to parse + * + * @access private + * @return array Array of tokens + */ function _tokenize($value) { - $tokens = array(); // array of tokens + $tokens = array(); // array of tokens $matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns - + // this one is taken from perl-ldap, modified for php $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x"; - + /** * This one matches one big pattern wherin only one of the three subpatterns matched * We are interested in the subpatterns that matched. If it matched its value will be @@ -350,6 +386,53 @@ } return $tokens; } - } -?> \ No newline at end of file + /** + * Returns wether a attribute syntax is binary or not + * + * This method gets used by Net_LDAP_Entry to decide which + * PHP function needs to be used to fetch the value in the + * proper format (e.g. binary or string) + * + * @param string $attribute The name of the attribute (eg.: 'sn') + * + * @access public + * @return boolean + */ + function isBinary($attribute) + { + $return = false; // default to false + + // This list contains all syntax that should be treaten as + // containing binary values + // The Syntax Definitons go into constants at the top of this page + $syntax_binary = array( + NET_LDAP_SYNTAX_OCTET_STRING, + NET_LDAP_SYNTAX_JPEG + ); + + // Check Syntax + $attr_s = $this->get('attribute', $attribute); + if (Net_LDAP::isError($attr_s)) { + // Attribute not found in schema + $return = false; // consider attr not binary + } elseif (isset($attr_s['syntax']) && in_array($attr_s['syntax'], $syntax_binary)) { + // Syntax is defined as binary in schema + $return = true; + } else { + // Syntax not defined as binary, or not found + // if attribute is a subtype, check superior attribute syntaxes + if (isset($attr_s['sup'])) { + foreach ($attr_s['sup'] as $superattr) { + $return = $this->isBinary($superattr); + if ($return) { + break; // stop checking parents since we are binary + } + } + } + } + + return $return; + } +} +?> diff --git a/thirdparty/pear/Net/LDAP/Search.php b/thirdparty/pear/Net/LDAP/Search.php old mode 100644 new mode 100755 index 7e3f117..8b6c164 --- a/thirdparty/pear/Net/LDAP/Search.php +++ b/thirdparty/pear/Net/LDAP/Search.php @@ -1,213 +1,446 @@ +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: Search.php,v 1.32 2008/10/26 15:31:06 clockwerx Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ +require_once 'PEAR.php'; /** - * Result set of an LDAP search - * - * @author Tarjei Huse - * @version $Revision$ - * @package Net_LDAP - */ +* Result set of an LDAP search +* +* @category Net +* @package Net_LDAP +* @author Tarjej Huse +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ class Net_LDAP_Search extends PEAR { /** - * Search result identifier - * - * @access private - * @var resource - */ + * Search result identifier + * + * @access private + * @var resource + */ var $_search; - + /** - * LDAP resource link - * - * @access private - * @var resource - */ + * LDAP resource link + * + * @access private + * @var resource + */ var $_link; /** - * Array of entries - * - * @access private - * @var array - */ - var $_entries = array(); - - /** - * Result entry identifier - * - * @access private - * @var resource - */ - var $_elink = null; - - /** - * The errorcode the search got - * - * Some errorcodes might be of interest, but might not be best handled as errors. - * examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indecates a huge search. - * Incomplete results are returned. If you just want to check if there's anything in the search. - * than this is a point to handle. - * 32 - no such object - search here returns a count of 0. - * - * @access private - * @var int - */ + * Net_LDAP object + * + * A reference of the Net_LDAP object for passing to Net_LDAP_Entry + * + * @access private + * @var object Net_LDAP + */ + var $_ldap; + + /** + * Result entry identifier + * + * @access private + * @var resource + */ + var $_entry = null; + + /** + * The errorcode the search got + * + * Some errorcodes might be of interest, but might not be best handled as errors. + * examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indicates a huge search. + * Incomplete results are returned. If you just want to check if there's anything in the search. + * than this is a point to handle. + * 32 - no such object - search here returns a count of 0. + * + * @access private + * @var int + */ var $_errorCode = 0; // if not set - sucess! - - /** + + /** + * What attributes we searched for + * + * The $attributes array contains the names of the searched attributes and gets + * passed from $Net_LDAP->search() so the Net_LDAP_Search object can tell + * what attributes was searched for ({@link _searchedAttrs()) + * + * This variable gets set from the constructor and returned + * from {@link _searchedAttrs()} + * + * @access private + * @var array + */ + var $_searchedAttrs = array(); + + /** + * Cache variable for storing entries fetched internally + * + * This currently is only used by {@link pop_entry()} + * + * @access private + * @var array + */ + var $_entry_cache = false; + + /** * Constructor * + * @param resource &$search Search result identifier + * @param Net_LDAP|resource &$ldap Net_LDAP object or just a LDAP-Link resource + * @param array $attributes (optional) Array with searched attribute names. (see {@link $_searchedAttrs}) + * * @access protected - * @param resource Search result identifier - * @param resource Link identifier */ - function Net_LDAP_Search (&$search, &$link) - { - $this->_setSearch($search, $link); - $this->_errorCode = ldap_errno($link); + function Net_LDAP_Search(&$search, &$ldap, $attributes = array()) + { + $this->PEAR('Net_LDAP_Error'); + + $this->setSearch($search); + + if (is_a($ldap, 'Net_LDAP')) { + $this->_ldap =& $ldap; + $this->setLink($this->_ldap->getLink()); + } else { + $this->setLink($ldap); + } + + $this->_errorCode = @ldap_errno($this->_link); + + if (is_array($attributes) && !empty($attributes)) { + $this->_searchedAttrs = $attributes; + } } /** - * Returns an assosiative array of entry objects - * - * @return array Array of entry objects. - */ + * Returns an array of entry objects + * + * @return array Array of entry objects. + */ function entries() { - if ($this->count() == 0) { - return array(); - } - - $this->_elink = @ldap_first_entry( $this->_link,$this->_search); - $entry = new Net_LDAP_Entry($this->_link, - @ldap_get_dn($this->_link, $this->_elink), - @ldap_get_attributes($this->_link, $this->_elink)); - array_push($this->_entries, $entry); + $entries = array(); - while ($this->_elink = @ldap_next_entry($this->_link,$this->_elink)) { - $entry = new Net_LDAP_Entry($this->_link, - @ldap_get_dn($this->_link, $this->_elink), - @ldap_get_attributes($this->_link, $this->_elink)); - array_push($this->_entries, $entry); + while ($entry = $this->shiftEntry()) { + $entries[] = $entry; } - return $this->_entries; + + return $entries; } - + /** - * Get the next entry in the searchresult. - * - * @return mixed Net_LDAP_Entry object or false - */ - function shiftEntry() + * Get the next entry in the searchresult. + * + * This will return a valid Net_LDAP_Entry object or false, so + * you can use this method to easily iterate over the entries inside + * a while loop. + * + * @return Net_LDAP_Entry|false Reference to Net_LDAP_Entry object or false + */ + function &shiftEntry() { if ($this->count() == 0 ) { - return false; + $false = false; + return $false; } - if (is_null($this->_elink)) { - $this->_elink = @ldap_first_entry($this->_link, $this->_search); - $entry = new Net_LDAP_Entry($this->_link, - ldap_get_dn($this->_link, $this->_elink), - ldap_get_attributes($this->_link, $this->_elink)); + if (is_null($this->_entry)) { + $this->_entry = @ldap_first_entry($this->_link, $this->_search); + $entry = new Net_LDAP_Entry($this->_ldap, $this->_entry); } else { - if (!$this->_elink = ldap_next_entry($this->_link, $this->_elink)) { - return false; + if (!$this->_entry = @ldap_next_entry($this->_link, $this->_entry)) { + $false = false; + return $false; } - $entry = new Net_LDAP_Entry($this->_link, - ldap_get_dn($this->_link,$this->_elink), - ldap_get_attributes($this->_link,$this->_elink)); + $entry = new Net_LDAP_Entry($this->_ldap, $this->_entry); } return $entry; } - + + /** + * Alias function of shiftEntry() for perl-ldap interface + * + * @see shiftEntry() + * @return Net_LDAP_Entry|false Reference to Net_LDAP_Entry object or false + */ + function shift_entry() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'shiftEntry' ), $args); + } + /** - * alias function of shiftEntry() for perl-ldap interface - * - * @see shiftEntry() - */ - function shift_entry() + * Retrieve the next entry in the searchresult, but starting from last entry + * + * This is the opposite to {@link shiftEntry()} and is also very useful + * to be used inside a while loop. + * + * @return Net_LDAP_Entry|false + */ + function popEntry() + { + if (false === $this->_entry_cache) { + // fetch entries into cache if not done so far + $this->_entry_cache = $this->entries(); + } + + $return = array_pop($this->_entry_cache); + return (null === $return)? false : $return; + } + + /** + * Alias function of popEntry() for perl-ldap interface + * + * @see popEntry() + * @return Net_LDAP_Entry|false + */ + function pop_entry() { $args = func_get_args(); - return call_user_func_array(array($this, 'shiftEntry'), $args); + return call_user_func_array(array( &$this, 'popEntry' ), $args); } - + /** - * Retrieve the last entry of the searchset. NOT IMPLEMENTED - * - * @return object Net_LDAP_Error - */ - function pop_entry () + * Return entries sorted as array + * + * This returns a array with sorted entries and the values. + * Sorting is done with PHPs {@link array_multisort()}. + * This method relies on {@link as_struct()} to fetch the raw data of the entries. + * + * Please note that attribute names are case sensitive! + * + * Usage example: + * + * // to sort entries first by location, then by surename, but descending: + * $entries = $search->sorted_as_struct(array('locality','sn'), SORT_DESC); + * + * + * @param array $attrs Array of attribute names to sort; order from left to right. + * @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC + * + * @return array|Net_LDAP_Error Array with sorted entries or error + */ + function sorted_as_struct($attrs = array('cn'), $order = SORT_ASC) { - $this->raiseError("Not implemented"); + /* + * Old Code, suitable and fast for single valued sorting + * This code should be used if we know that single valued sorting is desired, + * but we need some method to get that knowledge... + */ + /* + $attrs = array_reverse($attrs); + foreach ($attrs as $attribute) { + if (!ldap_sort($this->_link, $this->_search, $attribute)){ + $this->raiseError("Sorting failed for Attribute " . $attribute); + } + } + + $results = ldap_get_entries($this->_link, $this->_search); + + unset($results['count']); //for tidier output + if ($order) { + return array_reverse($results); + } else { + return $results; + }*/ + + /* + * New code: complete "client side" sorting + */ + // first some parameterchecks + if (!is_array($attrs)) { + return PEAR::raiseError("Sorting failed: Parameterlist must be an array!"); + } + if ($order != SORT_ASC && $order != SORT_DESC) { + return PEAR::raiseError("Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)"); + } + + // fetch the entries data + $entries = $this->as_struct(); + + // now sort each entries attribute values + // this is neccessary because later we can only sort by one value, + // so we need the highest or lowest attribute now, depending on the + // selected ordering for that specific attribute + foreach ($entries as $dn => $entry) { + foreach ($entry as $attr_name => $attr_values) { + sort($entries[$dn][$attr_name]); + if ($order == SORT_DESC) { + array_reverse($entries[$dn][$attr_name]); + } + } + } + + // reformat entrys array for later use with array_multisort() + $to_sort = array(); // <- will be a numeric array similar to ldap_get_entries + foreach ($entries as $dn => $entry_attr) { + $row = array(); + $row['dn'] = $dn; + foreach ($entry_attr as $attr_name => $attr_values) { + $row[$attr_name] = $attr_values; + } + $to_sort[] = $row; + } + + // Build columns for array_multisort() + // each requested attribute is one row + $columns = array(); + foreach ($attrs as $attr_name) { + foreach ($to_sort as $key => $row) { + $columns[$attr_name][$key] =& $to_sort[$key][$attr_name][0]; + } + } + + // sort the colums with array_multisort, if there is something + // to sort and if we have requested sort columns + if (!empty($to_sort) && !empty($columns)) { + $sort_params = ''; + foreach ($attrs as $attr_name) { + $sort_params .= '$columns[\''.$attr_name.'\'], '.$order.', '; + } + eval("array_multisort($sort_params \$to_sort);"); // perform sorting + } + + return $to_sort; } - + /** - * Return entries sorted NOT IMPLEMENTED - * - * @param array Array of sort attributes - * @return object Net_LDAP_Error - */ - function sorted ($attrs = array()) + * Return entries sorted as objects + * + * This returns a array with sorted Net_LDAP_Entry objects. + * The sorting is actually done with {@link sorted_as_struct()}. + * + * Please note that attribute names are case sensitive! + * + * Usage example: + * + * // to sort entries first by location, then by surename, but descending: + * $entries = $search->sorted(array('locality','sn'), SORT_DESC); + * + * + * @param array $attrs Array of sort attributes to sort; order from left to right. + * @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC + * + * @return array|Net_LDAP_Error Array with sorted Net_LDAP_Entries or error + */ + function sorted($attrs = array('cn'), $order = SORT_ASC) { - $this->raiseError("Not impelented"); + $return = array(); + $sorted = $this->sorted_as_struct($attrs, $order); + if (PEAR::isError($sorted)) { + return $sorted; + } + foreach ($sorted as $key => $row) { + $entry = $this->_ldap->getEntry($row['dn'], $this->_searchedAttrs()); + if (!PEAR::isError($entry)) { + array_push($return, $entry); + } else { + return $entry; + } + } + return $return; } - - /** - * Return entries as object NOT IMPLEMENTED + + /** + * Return entries as array + * + * This method returns the entries and the selected attributes values as + * array. + * The first array level contains all found entries where the keys are the + * DNs of the entries. The second level arrays contian the entries attributes + * such that the keys is the lowercased name of the attribute and the values + * are stored in another indexed array. Note that the attribute values are stored + * in an array even if there is no or just one value. + * + * The array has the following structure: + * + * $return = array( + * 'cn=foo,dc=example,dc=com' => array( + * 'sn' => array('foo'), + * 'multival' => array('val1', 'val2', 'valN') + * ) + * 'cn=bar,dc=example,dc=com' => array( + * 'sn' => array('bar'), + * 'multival' => array('val1', 'valN') + * ) + * ) + * * - * @return object Net_LDAP_Error + * @return array associative result array as described above */ - function as_struct () + function as_struct() { - $this->raiseError("Not implemented"); + $return = array(); + $entries = $this->entries(); + foreach ($entries as $entry) { + $attrs = array(); + $entry_attributes = $entry->attributes(); + foreach ($entry_attributes as $attr_name) { + $attr_values = $entry->getValue($attr_name, 'all'); + if (!is_array($attr_values)) { + $attr_values = array($attr_values); + } + $attrs[$attr_name] = $attr_values; + } + $return[$entry->dn()] = $attrs; + } + return $return; } - /** - * Set the searchobjects resourcelinks + /** + * Set the search objects resource link * - * @access private - * @param resource Search result identifier - * @param resource Resource link identifier + * @param resource &$search Search result identifier + * + * @access public + * @return void */ - function _setSearch(&$search,&$link) - { + function setSearch(&$search) + { $this->_search = $search; - $this->_link = $link; } - - /** + + /** + * Set the ldap ressource link + * + * @param resource &$link Link identifier + * + * @access public + * @return void + */ + function setLink(&$link) + { + $this->_link = $link; + } + + /** * Returns the number of entries in the searchresult * * @return int Number of entries in search. */ function count() { - /* this catches the situation where OL returned errno 32 = no such object! */ + // this catches the situation where OL returned errno 32 = no such object! if (!$this->_search) { return 0; } @@ -215,31 +448,56 @@ class Net_LDAP_Search extends PEAR } /** - * Get the errorcode the object got in its search. - * - * @return int The ldap error number. - */ + * Get the errorcode the object got in its search. + * + * @return int The ldap error number. + */ function getErrorCode() { return $this->_errorCode; } - - /** Destructor + + /** + * Destructor * * @access protected */ - function _Net_LDAP_Search() + function _Net_LDAP_Search() { @ldap_free_result($this->_search); } - - /** + + /** * Closes search result + * + * @return void */ function done() { $this->_Net_LDAP_Search(); } + + /** + * Return the attribute names this search selected + * + * @return array + * @see $_searchedAttrs + * @access private + */ + function _searchedAttrs() + { + return $this->_searchedAttrs; + } + + /** + * Tells if this search exceeds a sizelimit + * + * @return boolean + */ + function sizeLimitExceeded() + { + return ($this->getErrorCode() == 4); + } } ?> diff --git a/thirdparty/pear/Net/LDAP/Util.php b/thirdparty/pear/Net/LDAP/Util.php old mode 100644 new mode 100755 index 6a99169..c6c3b5e --- a/thirdparty/pear/Net/LDAP/Util.php +++ b/thirdparty/pear/Net/LDAP/Util.php @@ -1,132 +1,612 @@ - * @version $Revision$ - */ -class Net_LDAP_Util extends PEAR +* Util.php +* +* PHP version 4, 5 +* +* @category Net +* @package Net_LDAP +* @author Tarjej Huse +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @version CVS: $Id: Util.php,v 1.29 2008/10/26 15:31:06 clockwerx Exp $ +* @link http://pear.php.net/package/Net_LDAP/ +*/ +require_once 'PEAR.php'; + +/** +* Utility Class for Net_LDAP +* +* This class servers some functionality to the other classes of Net_LDAP but most of +* the methods can be used separately as well. +* +* @category Net +* @package Net_LDAP +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP/ +*/ +class Net_LDAP_Util extends PEAR { /** - * Reference to LDAP object - * - * @access private - * @var object Net_LDAP - */ - var $_ldap = null; - - /** - * Net_LDAP_Schema object - * - * @access private - * @var object Net_LDAP_Schema - */ - var $_schema = null; - + * Private empty Constructur + * + * @access private + */ + function Net_LDAP_Util() + { + // We do nothing here, since all methods can be called statically. + // In Net_LDAP <= 0.7, we needed a instance of Util, because + // it was possible to do utf8 encoding and decoding, but this + // has been moved to the LDAP class. The constructor remains only + // here to document the downward compatibility of creating a instance. + } + /** - * Constructur - * - * Takes an LDAP object by reference and saves it. Then the schema will be fetched. - * - * @access public - * @param object Net_LDAP - */ - function Net_LDAP_Util(&$ldap) + * Explodes the given DN into its elements + * + * {@link http://www.ietf.org/rfc/rfc2253.txt RFC 2253} says, a Distinguished Name is a sequence + * of Relative Distinguished Names (RDNs), which themselves + * are sets of Attributes. For each RDN a array is constructed where the RDN part is stored. + * + * For example, the DN 'OU=Sales+CN=J. Smith,DC=example,DC=net' is exploded to: + * array( [0] => array([0] => 'OU=Sales', [1] => 'CN=J. Smith'), [2] => 'DC=example', [3] => 'DC=net' ) + * + * [NOT IMPLEMENTED] DNs might also contain values, which are the bytes of the BER encoding of + * the X.500 AttributeValue rather than some LDAP string syntax. These values are hex-encoded + * and prefixed with a #. To distinguish such BER values, ldap_explode_dn uses references to + * the actual values, e.g. '1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com' is exploded to: + * [ { '1.3.6.1.4.1.1466.0' => "\004\002Hi" }, { 'DC' => 'example' }, { 'DC' => 'com' } ]; + * See {@link http://www.vijaymukhi.com/vmis/berldap.htm} for more information on BER. + * + * It also performs the following operations on the given DN: + * - Unescape "\" followed by ",", "+", """, "\", "<", ">", ";", "#", "=", " ", or a hexpair + * and strings beginning with "#". + * - Removes the leading 'OID.' characters if the type is an OID instead of a name. + * - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order. + * + * OPTIONS is a list of name/value pairs, valid options are: + * casefold Controls case folding of attribute types names. + * Attribute values are not affected by this option. + * The default is to uppercase. Valid values are: + * lower Lowercase attribute types names. + * upper Uppercase attribute type names. This is the default. + * none Do not change attribute type names. + * reverse If TRUE, the RDN sequence is reversed. + * onlyvalues If TRUE, then only attributes values are returned ('foo' instead of 'cn=foo') + * + + * @param string $dn The DN that should be exploded + * @param array $options Options to use + * + * @static + * @return array Parts of the exploded DN + * @todo implement BER + */ + function ldap_explode_dn($dn, $options = array('casefold' => 'upper')) { - if (is_object($ldap) && (strtolower(get_class($ldap)) == 'net_ldap')) { - $this->_ldap = $ldap; - $this->_schema = $this->_ldap->schema(); - if (Net_LDAP::isError($this->_schema)) $this->_schema = null; + if (!isset($options['onlyvalues'])) { + $options['onlyvalues'] = false; + } + + if (!isset($options['reverse'])) { + $options['reverse'] = false; + } + + if (!isset($options['casefold'])) { + $options['casefold'] = 'upper'; + } + + // Escaping of DN and stripping of "OID." + $dn = Net_LDAP_Util::canonical_dn($dn, array('casefold' => $options['casefold'])); + + // splitting the DN + $dn_array = preg_split('/(?<=[^\\\\]),/', $dn); + + // construct subarrays for multivalued RDNs and unescape DN value + // also convert to output format and apply casefolding + foreach ($dn_array as $key => $value) { + $value_u = Net_LDAP_Util::unescape_dn_value($value); + $rdns = Net_LDAP_Util::split_rdn_multival($value_u[0]); + if (count($rdns) > 1) { + // MV RDN! + foreach ($rdns as $subrdn_k => $subrdn_v) { + // Casefolding + if ($options['casefold'] == 'upper') { + $subrdn_v = preg_replace("/^(\w+=)/e", "''.strtoupper('\\1').''", $subrdn_v); + } + + if ($options['casefold'] == 'lower') { + $subrdn_v = preg_replace("/^(\w+=)/e", "''.strtolower('\\1').''", $subrdn_v); + } + + if ($options['onlyvalues']) { + preg_match('/(.+?)(?", ";", "#", "=" with a special meaning in RFC 2252 + * are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair. + * Finally all leading and trailing spaces are converted to sequences of \20. + * + * @param array $values An array containing the DN values that should be escaped + * + * @static + * @return array The array $values, but escaped + */ + function escape_dn_value($values = array()) { - return $this->_utf8($attributes, 'utf8_encode'); + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $val) { + // Escaping of filter meta characters + $val = str_replace('\\', '\\\\', $val); + $val = str_replace(',', '\,', $val); + $val = str_replace('+', '\+', $val); + $val = str_replace('"', '\"', $val); + $val = str_replace('<', '\<', $val); + $val = str_replace('>', '\>', $val); + $val = str_replace(';', '\;', $val); + $val = str_replace('#', '\#', $val); + $val = str_replace('=', '\=', $val); + + // ASCII < 32 escaping + $val = Net_LDAP_Util::asc2hex32($val); + + // Convert all leading and trailing spaces to sequences of \20. + if (preg_match('/^(\s*)(.+?)(\s*)$/', $val, $matches)) { + $val = $matches[2]; + for ($i = 0; $i < strlen($matches[1]); $i++) { + $val = '\20'.$val; + } + for ($i = 0; $i < strlen($matches[3]); $i++) { + $val = $val.'\20'; + } + } + + if (null === $val) { + $val = '\0'; // apply escaped "null" if string is empty + } + + $values[$key] = $val; + } + + return $values; } - + /** - * Decodes the given attribute values - * - * @access public - * @param array Array of attributes - * @return array Array with decoded attribute values - */ - function utf8Decode($attributes) + * Undoes the conversion done by escape_dn_value(). + * + * Any escape sequence starting with a baskslash - hexpair or special character - + * will be transformed back to the corresponding character. + * + * @param array $values Array of DN Values + * + * @return array Same as $values, but unescaped + * @static + */ + function unescape_dn_value($values = array()) { - return $this->_utf8($attributes, 'utf8_decode'); + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $val) { + // strip slashes from special chars + $val = str_replace('\\\\', '\\', $val); + + $val = str_replace('\,', ',', $val); + $val = str_replace('\+', '+', $val); + $val = str_replace('\"', '"', $val); + $val = str_replace('\<', '<', $val); + $val = str_replace('\>', '>', $val); + $val = str_replace('\;', ';', $val); + $val = str_replace('\#', '#', $val); + $val = str_replace('\=', '=', $val); + + // Translate hex code into ascii + $values[$key] = Net_LDAP_Util::hex2asc($val); + } + + return $values; } - + /** - * Encodes or decodes attribute values if needed - * - * @access private - * @param array Array of attributes - * @param array Function to apply to attribute values - * @return array Array of attributes with function applied to values - */ - function _utf8($attributes, $function) + * Returns the given DN in a canonical form + * + * Returns false if DN is not a valid Distinguished Name. + * DN can either be a string or an array + * as returned by ldap_explode_dn, which is useful when constructing a DN. + * The DN array may have be indexed (each array value is a OCL=VALUE pair) + * or associative (array key is OCL and value is VALUE). + * + * It performs the following operations on the given DN: + * - Removes the leading 'OID.' characters if the type is an OID instead of a name. + * - Escapes all RFC 2253 special characters (",", "+", """, "\", "<", ">", ";", "#", "="), slashes ("/"), and any other character where the ASCII code is < 32 as \hexpair. + * - Converts all leading and trailing spaces in values to be \20. + * - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order. + * + * OPTIONS is a list of name/value pairs, valid options are: + * casefold Controls case folding of attribute type names. + * Attribute values are not affected by this option. The default is to uppercase. + * Valid values are: + * lower Lowercase attribute type names. + * upper Uppercase attribute type names. This is the default. + * none Do not change attribute type names. + * [NOT IMPLEMENTED] mbcescape If TRUE, characters that are encoded as a multi-octet UTF-8 sequence will be escaped as \(hexpair){2,*}. + * reverse If TRUE, the RDN sequence is reversed. + * separator Separator to use between RDNs. Defaults to comma (','). + * + * Note: The empty string "" is a valid DN, so be sure not to do a "$can_dn == false" test, + * because an empty string evaluates to false. Use the "===" operator instead. + * + * @param array|string $dn The DN + * @param array $options Options to use + * + * @static + * @return false|string The canonical DN or FALSE + * @todo implement option mbcescape + */ + function canonical_dn($dn, $options = array('casefold' => 'upper', 'separator' => ',')) { - if (!$this->_ldap || !$this->_schema || !function_exists($function)) { - return $attributes; - } - if (is_array($attributes) && count($attributes) > 0) { - foreach( $attributes as $k => $v ) { - $attr = $this->_schema->get('attribute', $k); - if (Net_LDAP::isError($attr)) { - continue; - } - if (false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) { - if (is_array($v)) { - foreach ($v as $ak => $av ) { - $v[$ak] = call_user_func($function, $av ); + if ($dn === '') { + return $dn; // empty DN is valid! + } + + // options check + if (!isset($options['reverse'])) { + $options['reverse'] = false; + } else { + $options['reverse'] = true; + } + if (!isset($options['casefold'])) { + $options['casefold'] = 'upper'; + } + if (!isset($options['separator'])) { + $options['separator'] = ','; + } + + + if (!is_array($dn)) { + // It is not clear to me if the perl implementation splits by the user defined + // separator or if it just uses this separator to construct the new DN + $dn = preg_split('/(?<=[^\\\\])'.$options['separator'].'/', $dn); + + // clear wrong splitting (possibly we have split too much) + $dn = Net_LDAP_Util::_correct_dn_splitting($dn, $options['separator']); + } else { + // Is array, check, if the array is indexed or associative + $assoc = false; + foreach ($dn as $dn_key => $dn_part) { + if (!is_int($dn_key)) { + $assoc = true; + } + } + // convert to indexed, if associative array detected + if ($assoc) { + $newdn = array(); + foreach ($dn as $dn_key => $dn_part) { + if (is_array($dn_part)) { + ksort($dn_part, SORT_STRING); // we assume here, that the rdn parts are also associative + $newdn[] = $dn_part; // copy array as-is, so we can resolve it later + } else { + $newdn[] = $dn_key.'='.$dn_part; + } + } + $dn =& $newdn; + } + } + + // Escaping and casefolding + foreach ($dn as $pos => $dnval) { + if (is_array($dnval)) { + // subarray detected, this means very surely, that we had + // a multivalued dn part, which must be resolved + $dnval_new = ''; + foreach ($dnval as $subkey => $subval) { + // build RDN part + if (!is_int($subkey)) { + $subval = $subkey.'='.$subval; + } + $subval_processed = Net_LDAP_Util::canonical_dn($subval); + if (false === $subval_processed) { + return false; + } + $dnval_new .= $subval_processed.'+'; + } + $dn[$pos] = substr($dnval_new, 0, -1); // store RDN part, strip last plus + } else { + // try to split multivalued RDNS into array + $rdns = Net_LDAP_Util::split_rdn_multival($dnval); + if (count($rdns) > 1) { + // Multivalued RDN was detected! + // The RDN value is expected to be correctly split by split_rdn_multival(). + // It's time to sort the RDN and build the DN! + $rdn_string = ''; + sort($rdns, SORT_STRING); // Sort RDN keys alphabetically + foreach ($rdns as $rdn) { + $subval_processed = Net_LDAP_Util::canonical_dn($rdn); + if (false === $subval_processed) { + return false; } + $rdn_string .= $subval_processed.'+'; + } + + $dn[$pos] = substr($rdn_string, 0, -1); // store RDN part, strip last plus + + } else { + // no multivalued RDN! + // split at first unescaped "=" + $dn_comp = preg_split('/(?<=[^\\\\])=/', $rdns[0], 2); + $ocl = ltrim($dn_comp[0]); // trim left whitespaces 'cause of "cn=foo, l=bar" syntax (whitespace after comma) + $val = $dn_comp[1]; + + // strip 'OID.', otherwise apply casefolding and escaping + if (substr(strtolower($ocl), 0, 4) == 'oid.') { + $ocl = substr($ocl, 4); } else { - $v = call_user_func($function, $v); + if ($options['casefold'] == 'upper') { + $ocl = strtoupper($ocl); + } + if ($options['casefold'] == 'lower') { + $ocl = strtolower($ocl); + } + $ocl = Net_LDAP_Util::escape_dn_value(array($ocl)); + $ocl = $ocl[0]; } + + // escaping of dn-value + $val = Net_LDAP_Util::escape_dn_value(array($val)); + $val = str_replace('/', '\/', $val[0]); + + $dn[$pos] = $ocl.'='.$val; + } + } + } + + if ($options['reverse']) { + $dn = array_reverse($dn); + } + + return implode($options['separator'], $dn); + } + + /** + * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters. + * + * Any control characters with an ACII code < 32 as well as the characters with special meaning in + * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a + * backslash followed by two hex digits representing the hexadecimal value of the character. + * + * @param array $values Array of values to escape + * + * @static + * @return array Array $values, but escaped + */ + function escape_filter_value($values = array()) + { + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $val) { + // Escaping of filter meta characters + $val = str_replace('\\', '\5c', $val); + $val = str_replace('*', '\2a', $val); + $val = str_replace('(', '\28', $val); + $val = str_replace(')', '\29', $val); + + // ASCII < 32 escaping + $val = Net_LDAP_Util::asc2hex32($val); + + if (null === $val) { + $val = '\0'; // apply escaped "null" if string is empty + } + + $values[$key] = $val; + } + + return $values; + } + + /** + * Undoes the conversion done by {@link escape_filter_value()}. + * + * Converts any sequences of a backslash followed by two + * hex digits into the corresponding character. + * + * @param array $values Array of values to escape + * + * @static + * @return array Array $values, but unescaped + */ + function unescape_filter_value($values = array()) + { + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $value) { + // Translate hex code into ascii + $values[$key] = Net_LDAP_Util::hex2asc($value); + } + + return $values; + } + + /** + * Converts all ASCII chars < 32 to "\HEX" + * + * @param string $string String to convert + * + * @static + * @return string + */ + function asc2hex32($string) + { + for ($i = 0; $i < strlen($string); $i++) { + $char = substr($string, $i, 1); + if (ord($char) < 32) { + $hex = dechex(ord($char)); + if (strlen($hex) == 1) { + $hex = '0'.$hex; } - $attributes[$k] = $v; + $string = str_replace($char, '\\'.$hex, $string); } } - return $attributes; - } + return $string; + } + + /** + * Converts all Hex expressions ("\HEX") to their original ASCII characters + * + * @param string $string String to convert + * + * @static + * @author beni@php.net, heavily based on work from DavidSmith@byu.net + * @return string + */ + function hex2asc($string) + { + $string = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $string); + return $string; + } + + /** + * Split an multivalued RDN value into an Array + * + * A RDN can contain multiple values, spearated by a plus sign. + * This function returns each separate ocl=value pair of the RDN part. + * + * If no multivalued RDN is detected, a array containing only + * the original rdn part is returned. + * + * For example, the multivalued RDN 'OU=Sales+CN=J. Smith' is exploded to: + * array([0] => 'OU=Sales', [1] => 'CN=J. Smith') + * + * The method trys to be smart if it encounters unescaped "+" characters, but may fail, + * so ensure escaped "+"es in attr names and attr values. + * + * [BUG] If you use string mode and have a multivalued RDN with unescaped plus characters + * and there is a unescaped plus sign at the end of an value followed by an + * attribute name containing an unescaped plus, then you will get wrong splitting: + * $rdn = 'OU=Sales+C+N=J. Smith'; + * returns: + * array('OU=Sales+C', 'N=J. Smith'); + * The "C+" is treaten as value of the first pair instead as attr name of the second pair. + * To prevent this, escape correctly. + * + * @param string $rdn Part of an (multivalued) escaped RDN (eg. ou=foo OR ou=foo+cn=bar) + * + * @static + * @return array Array with the components of the multivalued RDN or Error + */ + function split_rdn_multival($rdn) + { + $rdns = preg_split('/(? $dn_value) { + $dn_value = $dn[$key]; // refresh value (foreach caches!) + // if the dn_value is not in attr=value format, then we had an + // unescaped separator character inside the attr name or the value. + // We assume, that it was the attribute value. + // [TODO] To solve this, we might ask the schema. Keep in mind, that UTIL class + // must remain independent from the other classes or connections. + if (!preg_match('/.+(? diff --git a/thirdparty/pear/Net/LDAP/fg.php b/thirdparty/pear/Net/LDAP/fg.php old mode 100644 new mode 100755 index a7f0bfd..a7f0bfd --- a/thirdparty/pear/Net/LDAP/fg.php +++ b/thirdparty/pear/Net/LDAP/fg.php diff --git a/thirdparty/pear/Net/LDAP2.php b/thirdparty/pear/Net/LDAP2.php new file mode 100644 index 0000000..26f5e75 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2.php @@ -0,0 +1,1791 @@ + +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: LDAP2.php 286788 2009-08-04 06:05:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Package includes. +*/ +require_once 'PEAR.php'; +require_once 'Net/LDAP2/RootDSE.php'; +require_once 'Net/LDAP2/Schema.php'; +require_once 'Net/LDAP2/Entry.php'; +require_once 'Net/LDAP2/Search.php'; +require_once 'Net/LDAP2/Util.php'; +require_once 'Net/LDAP2/Filter.php'; +require_once 'Net/LDAP2/LDIF.php'; +require_once 'Net/LDAP2/SchemaCache.interface.php'; +require_once 'Net/LDAP2/SimpleFileSchemaCache.php'; + +/** +* Error constants for errors that are not LDAP errors. +*/ +define('NET_LDAP2_ERROR', 1000); + +/** +* Net_LDAP2 Version +*/ +define('NET_LDAP2_VERSION', '2.0.7'); + +/** +* Net_LDAP2 - manipulate LDAP servers the right way! +* +* @category Net +* @package Net_LDAP2 +* @author Tarjej Huse +* @author Jan Wagner +* @author Del +* @author Benedikt Hallinger +* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP2/ +*/ +class Net_LDAP2 extends PEAR +{ + /** + * Class configuration array + * + * host = the ldap host to connect to + * (may be an array of several hosts to try) + * port = the server port + * version = ldap version (defaults to v 3) + * starttls = when set, ldap_start_tls() is run after connecting. + * bindpw = no explanation needed + * binddn = the DN to bind as. + * basedn = ldap base + * options = hash of ldap options to set (opt => val) + * filter = default search filter + * scope = default search scope + * + * Newly added in 2.0.0RC4, for auto-reconnect: + * auto_reconnect = if set to true then the class will automatically + * attempt to reconnect to the LDAP server in certain + * failure conditionswhen attempting a search, or other + * LDAP operation. Defaults to false. Note that if you + * set this to true, calls to search() may block + * indefinitely if there is a catastrophic server failure. + * min_backoff = minimum reconnection delay period (in seconds). + * current_backoff = initial reconnection delay period (in seconds). + * max_backoff = maximum reconnection delay period (in seconds). + * + * @access protected + * @var array + */ + protected $_config = array('host' => 'localhost', + 'port' => 389, + 'version' => 3, + 'starttls' => false, + 'binddn' => '', + 'bindpw' => '', + 'basedn' => '', + 'options' => array(), + 'filter' => '(objectClass=*)', + 'scope' => 'sub', + 'auto_reconnect' => false, + 'min_backoff' => 1, + 'current_backoff' => 1, + 'max_backoff' => 32); + + /** + * List of hosts we try to establish a connection to + * + * @access protected + * @var array + */ + protected $_host_list = array(); + + /** + * List of hosts that are known to be down. + * + * @access protected + * @var array + */ + protected $_down_host_list = array(); + + /** + * LDAP resource link. + * + * @access protected + * @var resource + */ + protected $_link = false; + + /** + * Net_LDAP2_Schema object + * + * This gets set and returned by {@link schema()} + * + * @access protected + * @var object Net_LDAP2_Schema + */ + protected $_schema = null; + + /** + * Schema cacher function callback + * + * @see registerSchemaCache() + * @var string + */ + protected $_schema_cache = null; + + /** + * Cache for attribute encoding checks + * + * @access protected + * @var array Hash with attribute names as key and boolean value + * to determine whether they should be utf8 encoded or not. + */ + protected $_schemaAttrs = array(); + + /** + * Cache for rootDSE objects + * + * Hash with requested rootDSE attr names as key and rootDSE object as value + * + * Since the RootDSE object itself may request a rootDSE object, + * {@link rootDse()} caches successful requests. + * Internally, Net_LDAP2 needs several lookups to this object, so + * caching increases performance significally. + * + * @access protected + * @var array + */ + protected $_rootDSE_cache = array(); + + /** + * Returns the Net_LDAP2 Release version, may be called statically + * + * @static + * @return string Net_LDAP2 version + */ + public static function getVersion() + { + return NET_LDAP2_VERSION; + } + + /** + * Configure Net_LDAP2, connect and bind + * + * Use this method as starting point of using Net_LDAP2 + * to establish a connection to your LDAP server. + * + * Static function that returns either an error object or the new Net_LDAP2 + * object. Something like a factory. Takes a config array with the needed + * parameters. + * + * @param array $config Configuration array + * + * @access public + * @return Net_LDAP2_Error|Net_LDAP2 Net_LDAP2_Error or Net_LDAP2 object + */ + public static function &connect($config = array()) + { + $ldap_check = self::checkLDAPExtension(); + if (self::iserror($ldap_check)) { + return $ldap_check; + } + + @$obj = new Net_LDAP2($config); + + // todo? better errorhandling for setConfig()? + + // connect and bind with credentials in config + $err = $obj->bind(); + if (self::isError($err)) { + return $err; + } + + return $obj; + } + + /** + * Net_LDAP2 constructor + * + * Sets the config array + * + * Please note that the usual way of getting Net_LDAP2 to work is + * to call something like: + * $ldap = Net_LDAP2::connect($ldap_config); + * + * @param array $config Configuration array + * + * @access protected + * @return void + * @see $_config + */ + public function __construct($config = array()) + { + $this->PEAR('Net_LDAP2_Error'); + $this->setConfig($config); + } + + /** + * Sets the internal configuration array + * + * @param array $config Configuration array + * + * @access protected + * @return void + */ + protected function setConfig($config) + { + // + // Parameter check -- probably should raise an error here if config + // is not an array. + // + if (! is_array($config)) { + return; + } + + foreach ($config as $k => $v) { + if (isset($this->_config[$k])) { + $this->_config[$k] = $v; + } else { + // map old (Net_LDAP2) parms to new ones + switch($k) { + case "dn": + $this->_config["binddn"] = $v; + break; + case "password": + $this->_config["bindpw"] = $v; + break; + case "tls": + $this->_config["starttls"] = $v; + break; + case "base": + $this->_config["basedn"] = $v; + break; + } + } + } + + // + // Ensure the host list is an array. + // + if (is_array($this->_config['host'])) { + $this->_host_list = $this->_config['host']; + } else { + if (strlen($this->_config['host']) > 0) { + $this->_host_list = array($this->_config['host']); + } else { + $this->_host_list = array(); + // ^ this will cause an error in performConnect(), + // so the user is notified about the failure + } + } + + // + // Reset the down host list, which seems like a sensible thing to do + // if the config is being reset for some reason. + // + $this->_down_host_list = array(); + } + + /** + * Bind or rebind to the ldap-server + * + * This function binds with the given dn and password to the server. In case + * no connection has been made yet, it will be started and startTLS issued + * if appropiate. + * + * The internal bind configuration is not being updated, so if you call + * bind() without parameters, you can rebind with the credentials + * provided at first connecting to the server. + * + * @param string $dn Distinguished name for binding + * @param string $password Password for binding + * + * @access public + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + public function bind($dn = null, $password = null) + { + // fetch current bind credentials + if (is_null($dn)) { + $dn = $this->_config["binddn"]; + } + if (is_null($password)) { + $password = $this->_config["bindpw"]; + } + + // Connect first, if we haven't so far. + // This will also bind us to the server. + if ($this->_link === false) { + // store old credentials so we can revert them later + // then overwrite config with new bind credentials + $olddn = $this->_config["binddn"]; + $oldpw = $this->_config["bindpw"]; + + // overwrite bind credentials in config + // so performConnect() knows about them + $this->_config["binddn"] = $dn; + $this->_config["bindpw"] = $password; + + // try to connect with provided credentials + $msg = $this->performConnect(); + + // reset to previous config + $this->_config["binddn"] = $olddn; + $this->_config["bindpw"] = $oldpw; + + // see if bind worked + if (self::isError($msg)) { + return $msg; + } + } else { + // do the requested bind as we are + // asked to bind manually + if (is_null($dn)) { + // anonymous bind + $msg = @ldap_bind($this->_link); + } else { + // privileged bind + $msg = @ldap_bind($this->_link, $dn, $password); + } + if (false === $msg) { + return PEAR::raiseError("Bind failed: " . + @ldap_error($this->_link), + @ldap_errno($this->_link)); + } + } + return true; + } + + /** + * Connect to the ldap-server + * + * This function connects to the LDAP server specified in + * the configuration, binds and set up the LDAP protocol as needed. + * + * @access protected + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + protected function performConnect() + { + // Note: Connecting is briefly described in RFC1777. + // Basicly it works like this: + // 1. set up TCP connection + // 2. secure that connection if neccessary + // 3a. setLDAPVersion to tell server which version we want to speak + // 3b. perform bind + // 3c. setLDAPVersion to tell server which version we want to speak + // together with a test for supported versions + // 4. set additional protocol options + + // Return true if we are already connected. + if ($this->_link !== false) { + return true; + } + + // Connnect to the LDAP server if we are not connected. Note that + // with some LDAP clients, ldapperformConnect returns a link value even + // if no connection is made. We need to do at least one anonymous + // bind to ensure that a connection is actually valid. + // + // Ref: http://www.php.net/manual/en/function.ldap-connect.php + + // Default error message in case all connection attempts + // fail but no message is set + $current_error = new PEAR_Error('Unknown connection error'); + + // Catch empty $_host_list arrays. + if (!is_array($this->_host_list) || count($this->_host_list) == 0) { + $current_error = PEAR::raiseError('No Servers configured! Please '. + 'pass in an array of servers to Net_LDAP2'); + return $current_error; + } + + // Cycle through the host list. + foreach ($this->_host_list as $host) { + + // Ensure we have a valid string for host name + if (is_array($host)) { + $current_error = PEAR::raiseError('No Servers configured! '. + 'Please pass in an one dimensional array of servers to '. + 'Net_LDAP2! (multidimensional array detected!)'); + continue; + } + + // Skip this host if it is known to be down. + if (in_array($host, $this->_down_host_list)) { + continue; + } + + // Record the host that we are actually connecting to in case + // we need it later. + $this->_config['host'] = $host; + + // Attempt a connection. + $this->_link = @ldap_connect($host, $this->_config['port']); + if (false === $this->_link) { + $current_error = PEAR::raiseError('Could not connect to ' . + $host . ':' . $this->_config['port']); + $this->_down_host_list[] = $host; + continue; + } + + // If we're supposed to use TLS, do so before we try to bind, + // as some strict servers only allow binding via secure connections + if ($this->_config["starttls"] === true) { + if (self::isError($msg = $this->startTLS())) { + $current_error = $msg; + $this->_link = false; + $this->_down_host_list[] = $host; + continue; + } + } + + // Try to set the configured LDAP version on the connection if LDAP + // server needs that before binding (eg OpenLDAP). + // This could be necessary since rfc-1777 states that the protocol version + // has to be set at the bind request. + // We use force here which means that the test in the rootDSE is skipped; + // this is neccessary, because some strict LDAP servers only allow to + // read the LDAP rootDSE (which tells us the supported protocol versions) + // with authenticated clients. + // This may fail in which case we try again after binding. + // In this case, most probably the bind() or setLDAPVersion()-call + // below will also fail, providing error messages. + $version_set = false; + $ignored_err = $this->setLDAPVersion(0, true); + if (!self::isError($ignored_err)) { + $version_set = true; + } + + // Attempt to bind to the server. If we have credentials configured, + // we try to use them, otherwise its an anonymous bind. + // As stated by RFC-1777, the bind request should be the first + // operation to be performed after the connection is established. + // This may give an protocol error if the server does not support + // V2 binds and the above call to setLDAPVersion() failed. + // In case the above call failed, we try an V2 bind here and set the + // version afterwards (with checking to the rootDSE). + $msg = $this->bind(); + if (self::isError($msg)) { + // The bind failed, discard link and save error msg. + // Then record the host as down and try next one + if ($msg->getCode() == 0x02 && !$version_set) { + // provide a finer grained error message + // if protocol error arieses because of invalid version + $msg = new Net_LDAP2_Error($msg->getMessage(). + " (could not set LDAP protocol version to ". + $this->_config['version'].")", + $msg->getCode()); + } + $this->_link = false; + $current_error = $msg; + $this->_down_host_list[] = $host; + continue; + } + + // Set desired LDAP version if not successfully set before. + // Here, a check against the rootDSE is performed, so we get a + // error message if the server does not support the version. + // The rootDSE entry should tell us which LDAP versions are + // supported. However, some strict LDAP servers only allow + // bound suers to read the rootDSE. + if (!$version_set) { + if (self::isError($msg = $this->setLDAPVersion())) { + $current_error = $msg; + $this->_link = false; + $this->_down_host_list[] = $host; + continue; + } + } + + // Set LDAP parameters, now we know we have a valid connection. + if (isset($this->_config['options']) && + is_array($this->_config['options']) && + count($this->_config['options'])) { + foreach ($this->_config['options'] as $opt => $val) { + $err = $this->setOption($opt, $val); + if (self::isError($err)) { + $current_error = $err; + $this->_link = false; + $this->_down_host_list[] = $host; + continue 2; + } + } + } + + // At this stage we have connected, bound, and set up options, + // so we have a known good LDAP server. Time to go home. + return true; + } + + + // All connection attempts have failed, return the last error. + return $current_error; + } + + /** + * Reconnect to the ldap-server. + * + * In case the connection to the LDAP + * service has dropped out for some reason, this function will reconnect, + * and re-bind if a bind has been attempted in the past. It is probably + * most useful when the server list provided to the new() or connect() + * function is an array rather than a single host name, because in that + * case it will be able to connect to a failover or secondary server in + * case the primary server goes down. + * + * This doesn't return anything, it just tries to re-establish + * the current connection. It will sleep for the current backoff + * period (seconds) before attempting the connect, and if the + * connection fails it will double the backoff period, but not + * try again. If you want to ensure a reconnection during a + * transient period of server downtime then you need to call this + * function in a loop. + * + * @access protected + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + protected function performReconnect() + { + + // Return true if we are already connected. + if ($this->_link !== false) { + return true; + } + + // Default error message in case all connection attempts + // fail but no message is set + $current_error = new PEAR_Error('Unknown connection error'); + + // Sleep for a backoff period in seconds. + sleep($this->_config['current_backoff']); + + // Retry all available connections. + $this->_down_host_list = array(); + $msg = $this->performConnect(); + + // Bail out if that fails. + if (self::isError($msg)) { + $this->_config['current_backoff'] = + $this->_config['current_backoff'] * 2; + if ($this->_config['current_backoff'] > $this->_config['max_backoff']) { + $this->_config['current_backoff'] = $this->_config['max_backoff']; + } + return $msg; + } + + // Now we should be able to safely (re-)bind. + $msg = $this->bind(); + if (self::isError($msg)) { + $this->_config['current_backoff'] = $this->_config['current_backoff'] * 2; + if ($this->_config['current_backoff'] > $this->_config['max_backoff']) { + $this->_config['current_backoff'] = $this->_config['max_backoff']; + } + + // _config['host'] should have had the last connected host stored in it + // by performConnect(). Since we are unable to bind to that host we can safely + // assume that it is down or has some other problem. + $this->_down_host_list[] = $this->_config['host']; + return $msg; + } + + // At this stage we have connected, bound, and set up options, + // so we have a known good LDAP server. Time to go home. + $this->_config['current_backoff'] = $this->_config['min_backoff']; + return true; + } + + /** + * Starts an encrypted session + * + * @access public + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + public function startTLS() + { + // Test to see if the server supports TLS first. + // This is done via testing the extensions offered by the server. + // The OID 1.3.6.1.4.1.1466.20037 tells us, if TLS is supported. + $rootDSE = $this->rootDse(); + if (self::isError($rootDSE)) { + return $this->raiseError("Unable to fetch rootDSE entry ". + "to see if TLS is supoported: ".$rootDSE->getMessage(), $rootDSE->getCode()); + } + + $supported_extensions = $rootDSE->getValue('supportedExtension'); + if (self::isError($supported_extensions)) { + return $this->raiseError("Unable to fetch rootDSE attribute 'supportedExtension' ". + "to see if TLS is supoported: ".$supported_extensions->getMessage(), $supported_extensions->getCode()); + } + + if (in_array('1.3.6.1.4.1.1466.20037', $supported_extensions)) { + if (false === @ldap_start_tls($this->_link)) { + return $this->raiseError("TLS not started: " . + @ldap_error($this->_link), + @ldap_errno($this->_link)); + } + return true; + } else { + return $this->raiseError("Server reports that it does not support TLS"); + } + } + + /** + * alias function of startTLS() for perl-ldap interface + * + * @return void + * @see startTLS() + */ + public function start_tls() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'startTLS' ), $args); + } + + /** + * Close LDAP connection. + * + * Closes the connection. Use this when the session is over. + * + * @return void + */ + public function done() + { + $this->_Net_LDAP2(); + } + + /** + * Alias for {@link done()} + * + * @return void + * @see done() + */ + public function disconnect() + { + $this->done(); + } + + /** + * Destructor + * + * @access protected + */ + public function _Net_LDAP2() + { + @ldap_close($this->_link); + } + + /** + * Add a new entryobject to a directory. + * + * Use add to add a new Net_LDAP2_Entry object to the directory. + * This also links the entry to the connection used for the add, + * if it was a fresh entry ({@link Net_LDAP2_Entry::createFresh()}) + * + * @param Net_LDAP2_Entry &$entry Net_LDAP2_Entry + * + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + public function add(&$entry) + { + if (!$entry instanceof Net_LDAP2_Entry) { + return PEAR::raiseError('Parameter to Net_LDAP2::add() must be a Net_LDAP2_Entry object.'); + } + + // Continue attempting the add operation in a loop until we + // get a success, a definitive failure, or the world ends. + $foo = 0; + while (true) { + $link = $this->getLink(); + + if ($link === false) { + // We do not have a successful connection yet. The call to + // getLink() would have kept trying if we wanted one. Go + // home now. + return PEAR::raiseError("Could not add entry " . $entry->dn() . + " no valid LDAP connection could be found."); + } + + if (@ldap_add($link, $entry->dn(), $entry->getValues())) { + // entry successfully added, we should update its $ldap reference + // in case it is not set so far (fresh entry) + if (!$entry->getLDAP() instanceof Net_LDAP2) { + $entry->setLDAP($this); + } + // store, that the entry is present inside the directory + $entry->markAsNew(false); + return true; + } else { + // We have a failure. What type? We may be able to reconnect + // and try again. + $error_code = @ldap_errno($link); + $error_name = $this->errorMessage($error_code); + + if (($error_name === 'LDAP_OPERATIONS_ERROR') && + ($this->_config['auto_reconnect'])) { + + // The server has become disconnected before trying the + // operation. We should try again, possibly with a different + // server. + $this->_link = false; + $this->performReconnect(); + } else { + // Errors other than the above catched are just passed + // back to the user so he may react upon them. + return PEAR::raiseError("Could not add entry " . $entry->dn() . " " . + $error_name, + $error_code); + } + } + } + } + + /** + * Delete an entry from the directory + * + * The object may either be a string representing the dn or a Net_LDAP2_Entry + * object. When the boolean paramter recursive is set, all subentries of the + * entry will be deleted as well. + * + * @param string|Net_LDAP2_Entry $dn DN-string or Net_LDAP2_Entry + * @param boolean $recursive Should we delete all children recursive as well? + * + * @access public + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + public function delete($dn, $recursive = false) + { + if ($dn instanceof Net_LDAP2_Entry) { + $dn = $dn->dn(); + } + if (false === is_string($dn)) { + return PEAR::raiseError("Parameter is not a string nor an entry object!"); + } + // Recursive delete searches for children and calls delete for them + if ($recursive) { + $result = @ldap_list($this->_link, $dn, '(objectClass=*)', array(null), 0, 0); + if (@ldap_count_entries($this->_link, $result)) { + $subentry = @ldap_first_entry($this->_link, $result); + $this->delete(@ldap_get_dn($this->_link, $subentry), true); + while ($subentry = @ldap_next_entry($this->_link, $subentry)) { + $this->delete(@ldap_get_dn($this->_link, $subentry), true); + } + } + } + + // Continue attempting the delete operation in a loop until we + // get a success, a definitive failure, or the world ends. + while (true) { + $link = $this->getLink(); + + if ($link === false) { + // We do not have a successful connection yet. The call to + // getLink() would have kept trying if we wanted one. Go + // home now. + return PEAR::raiseError("Could not add entry " . $dn . + " no valid LDAP connection could be found."); + } + + if (@ldap_delete($link, $dn)) { + // entry successfully deleted. + return true; + } else { + // We have a failure. What type? + // We may be able to reconnect and try again. + $error_code = @ldap_errno($link); + $error_name = $this->errorMessage($error_code); + + if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') && + ($this->_config['auto_reconnect'])) { + // The server has become disconnected before trying the + // operation. We should try again, possibly with a + // different server. + $this->_link = false; + $this->performReconnect(); + + } elseif ($error_code == 66) { + // Subentries present, server refused to delete. + // Deleting subentries is the clients responsibility, but + // since the user may not know of the subentries, we do not + // force that here but instead notify the developer so he + // may take actions himself. + return PEAR::raiseError("Could not delete entry $dn because of subentries. Use the recursive parameter to delete them."); + + } else { + // Errors other than the above catched are just passed + // back to the user so he may react upon them. + return PEAR::raiseError("Could not delete entry " . $dn . " " . + $error_name, + $error_code); + } + } + } + } + + /** + * Modify an ldapentry directly on the server + * + * This one takes the DN or a Net_LDAP2_Entry object and an array of actions. + * This array should be something like this: + * + * array('add' => array('attribute1' => array('val1', 'val2'), + * 'attribute2' => array('val1')), + * 'delete' => array('attribute1'), + * 'replace' => array('attribute1' => array('val1')), + * 'changes' => array('add' => ..., + * 'replace' => ..., + * 'delete' => array('attribute1', 'attribute2' => array('val1'))) + * + * The changes array is there so the order of operations can be influenced + * (the operations are done in order of appearance). + * The order of execution is as following: + * 1. adds from 'add' array + * 2. deletes from 'delete' array + * 3. replaces from 'replace' array + * 4. changes (add, replace, delete) in order of appearance + * All subarrays (add, replace, delete, changes) may be given at the same time. + * + * The function calls the corresponding functions of an Net_LDAP2_Entry + * object. A detailed description of array structures can be found there. + * + * Unlike the modification methods provided by the Net_LDAP2_Entry object, + * this method will instantly carry out an update() after each operation, + * thus modifying "directly" on the server. + * + * @param string|Net_LDAP2_Entry $entry DN-string or Net_LDAP2_Entry + * @param array $parms Array of changes + * + * @access public + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + public function modify($entry, $parms = array()) + { + if (is_string($entry)) { + $entry = $this->getEntry($entry); + if (self::isError($entry)) { + return $entry; + } + } + if (!$entry instanceof Net_LDAP2_Entry) { + return PEAR::raiseError("Parameter is not a string nor an entry object!"); + } + + // Perform changes mentioned separately + foreach (array('add', 'delete', 'replace') as $action) { + if (isset($parms[$action])) { + $msg = $entry->$action($parms[$action]); + if (self::isError($msg)) { + return $msg; + } + $entry->setLDAP($this); + + // Because the @ldap functions are called inside Net_LDAP2_Entry::update(), + // we have to trap the error codes issued from that if we want to support + // reconnection. + while (true) { + $msg = $entry->update(); + + if (self::isError($msg)) { + // We have a failure. What type? We may be able to reconnect + // and try again. + $error_code = $msg->getCode(); + $error_name = $this->errorMessage($error_code); + + if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') && + ($this->_config['auto_reconnect'])) { + + // The server has become disconnected before trying the + // operation. We should try again, possibly with a different + // server. + $this->_link = false; + $this->performReconnect(); + + } else { + + // Errors other than the above catched are just passed + // back to the user so he may react upon them. + return PEAR::raiseError("Could not modify entry: ".$msg->getMessage()); + } + } else { + // modification succeedet, evaluate next change + break; + } + } + } + } + + // perform combined changes in 'changes' array + if (isset($parms['changes']) && is_array($parms['changes'])) { + foreach ($parms['changes'] as $action => $value) { + + // Because the @ldap functions are called inside Net_LDAP2_Entry::update, + // we have to trap the error codes issued from that if we want to support + // reconnection. + while (true) { + $msg = $this->modify($entry, array($action => $value)); + + if (self::isError($msg)) { + // We have a failure. What type? We may be able to reconnect + // and try again. + $error_code = $msg->getCode(); + $error_name = $this->errorMessage($error_code); + + if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') && + ($this->_config['auto_reconnect'])) { + + // The server has become disconnected before trying the + // operation. We should try again, possibly with a different + // server. + $this->_link = false; + $this->performReconnect(); + + } else { + // Errors other than the above catched are just passed + // back to the user so he may react upon them. + return $msg; + } + } else { + // modification succeedet, evaluate next change + break; + } + } + } + } + + return true; + } + + /** + * Run a ldap search query + * + * Search is used to query the ldap-database. + * $base and $filter may be ommitted. The one from config will + * then be used. $base is either a DN-string or an Net_LDAP2_Entry + * object in which case its DN willb e used. + * + * Params may contain: + * + * scope: The scope which will be used for searching + * base - Just one entry + * sub - The whole tree + * one - Immediately below $base + * sizelimit: Limit the number of entries returned (default: 0 = unlimited), + * timelimit: Limit the time spent for searching (default: 0 = unlimited), + * attrsonly: If true, the search will only return the attribute names, + * attributes: Array of attribute names, which the entry should contain. + * It is good practice to limit this to just the ones you need. + * [NOT IMPLEMENTED] + * deref: By default aliases are dereferenced to locate the base object for the search, but not when + * searching subordinates of the base object. This may be changed by specifying one of the + * following values: + * + * never - Do not dereference aliases in searching or in locating the base object of the search. + * search - Dereference aliases in subordinates of the base object in searching, but not in + * locating the base object of the search. + * find + * always + * + * Please note, that you cannot override server side limitations to sizelimit + * and timelimit: You can always only lower a given limit. + * + * @param string|Net_LDAP2_Entry $base LDAP searchbase + * @param string|Net_LDAP2_Filter $filter LDAP search filter or a Net_LDAP2_Filter object + * @param array $params Array of options + * + * @access public + * @return Net_LDAP2_Search|Net_LDAP2_Error Net_LDAP2_Search object or Net_LDAP2_Error object + * @todo implement search controls (sorting etc) + */ + public function search($base = null, $filter = null, $params = array()) + { + if (is_null($base)) { + $base = $this->_config['basedn']; + } + if ($base instanceof Net_LDAP2_Entry) { + $base = $base->dn(); // fetch DN of entry, making searchbase relative to the entry + } + if (is_null($filter)) { + $filter = $this->_config['filter']; + } + if ($filter instanceof Net_LDAP2_Filter) { + $filter = $filter->asString(); // convert Net_LDAP2_Filter to string representation + } + if (PEAR::isError($filter)) { + return $filter; + } + if (PEAR::isError($base)) { + return $base; + } + + /* setting searchparameters */ + (isset($params['sizelimit'])) ? $sizelimit = $params['sizelimit'] : $sizelimit = 0; + (isset($params['timelimit'])) ? $timelimit = $params['timelimit'] : $timelimit = 0; + (isset($params['attrsonly'])) ? $attrsonly = $params['attrsonly'] : $attrsonly = 0; + (isset($params['attributes'])) ? $attributes = $params['attributes'] : $attributes = array(); + + // Ensure $attributes to be an array in case only one + // attribute name was given as string + if (!is_array($attributes)) { + $attributes = array($attributes); + } + + // reorganize the $attributes array index keys + // sometimes there are problems with not consecutive indexes + $attributes = array_values($attributes); + + // scoping makes searches faster! + $scope = (isset($params['scope']) ? $params['scope'] : $this->_config['scope']); + + switch ($scope) { + case 'one': + $search_function = 'ldap_list'; + break; + case 'base': + $search_function = 'ldap_read'; + break; + default: + $search_function = 'ldap_search'; + } + + // Continue attempting the search operation until we get a success + // or a definitive failure. + while (true) { + $link = $this->getLink(); + $search = @call_user_func($search_function, + $link, + $base, + $filter, + $attributes, + $attrsonly, + $sizelimit, + $timelimit); + + if ($err = @ldap_errno($link)) { + if ($err == 32) { + // Errorcode 32 = no such object, i.e. a nullresult. + return $obj = new Net_LDAP2_Search ($search, $this, $attributes); + } elseif ($err == 4) { + // Errorcode 4 = sizelimit exeeded. + return $obj = new Net_LDAP2_Search ($search, $this, $attributes); + } elseif ($err == 87) { + // bad search filter + return $this->raiseError($this->errorMessage($err) . "($filter)", $err); + } elseif (($err == 1) && ($this->_config['auto_reconnect'])) { + // Errorcode 1 = LDAP_OPERATIONS_ERROR but we can try a reconnect. + $this->_link = false; + $this->performReconnect(); + } else { + $msg = "\nParameters:\nBase: $base\nFilter: $filter\nScope: $scope"; + return $this->raiseError($this->errorMessage($err) . $msg, $err); + } + } else { + return $obj = new Net_LDAP2_Search($search, $this, $attributes); + } + } + } + + /** + * Set an LDAP option + * + * @param string $option Option to set + * @param mixed $value Value to set Option to + * + * @access public + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + */ + public function setOption($option, $value) + { + if ($this->_link) { + if (defined($option)) { + if (@ldap_set_option($this->_link, constant($option), $value)) { + return true; + } else { + $err = @ldap_errno($this->_link); + if ($err) { + $msg = @ldap_err2str($err); + } else { + $err = NET_LDAP2_ERROR; + $msg = $this->errorMessage($err); + } + return $this->raiseError($msg, $err); + } + } else { + return $this->raiseError("Unkown Option requested"); + } + } else { + return $this->raiseError("Could not set LDAP option: No LDAP connection"); + } + } + + /** + * Get an LDAP option value + * + * @param string $option Option to get + * + * @access public + * @return Net_LDAP2_Error|string Net_LDAP2_Error or option value + */ + public function getOption($option) + { + if ($this->_link) { + if (defined($option)) { + if (@ldap_get_option($this->_link, constant($option), $value)) { + return $value; + } else { + $err = @ldap_errno($this->_link); + if ($err) { + $msg = @ldap_err2str($err); + } else { + $err = NET_LDAP2_ERROR; + $msg = $this->errorMessage($err); + } + return $this->raiseError($msg, $err); + } + } else { + $this->raiseError("Unkown Option requested"); + } + } else { + $this->raiseError("No LDAP connection"); + } + } + + /** + * Get the LDAP_PROTOCOL_VERSION that is used on the connection. + * + * A lot of ldap functionality is defined by what protocol version the ldap server speaks. + * This might be 2 or 3. + * + * @return int + */ + public function getLDAPVersion() + { + if ($this->_link) { + $version = $this->getOption("LDAP_OPT_PROTOCOL_VERSION"); + } else { + $version = $this->_config['version']; + } + return $version; + } + + /** + * Set the LDAP_PROTOCOL_VERSION that is used on the connection. + * + * @param int $version LDAP-version that should be used + * @param boolean $force If set to true, the check against the rootDSE will be skipped + * + * @return Net_LDAP2_Error|true Net_LDAP2_Error object or true + * @todo Checking via the rootDSE takes much time - why? fetching and instanciation is quick! + */ + public function setLDAPVersion($version = 0, $force = false) + { + if (!$version) { + $version = $this->_config['version']; + } + + // + // Check to see if the server supports this version first. + // + // Todo: Why is this so horribly slow? + // $this->rootDse() is very fast, as well as Net_LDAP2_RootDSE::fetch() + // seems like a problem at copiyng the object inside PHP?? + // Additionally, this is not always reproducable... + // + if (!$force) { + $rootDSE = $this->rootDse(); + if ($rootDSE instanceof Net_LDAP2_Error) { + return $rootDSE; + } else { + $supported_versions = $rootDSE->getValue('supportedLDAPVersion'); + if (is_string($supported_versions)) { + $supported_versions = array($supported_versions); + } + $check_ok = in_array($version, $supported_versions); + } + } + + if ($force || $check_ok) { + return $this->setOption("LDAP_OPT_PROTOCOL_VERSION", $version); + } else { + return $this->raiseError("LDAP Server does not support protocol version " . $version); + } + } + + + /** + * Tells if a DN does exist in the directory + * + * @param string|Net_LDAP2_Entry $dn The DN of the object to test + * + * @return boolean|Net_LDAP2_Error + */ + public function dnExists($dn) + { + if (PEAR::isError($dn)) { + return $dn; + } + if ($dn instanceof Net_LDAP2_Entry) { + $dn = $dn->dn(); + } + if (false === is_string($dn)) { + return PEAR::raiseError('Parameter $dn is not a string nor an entry object!'); + } + + // make dn relative to parent + $base = Net_LDAP2_Util::ldap_explode_dn($dn, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false)); + if (self::isError($base)) { + return $base; + } + $entry_rdn = array_shift($base); + if (is_array($entry_rdn)) { + // maybe the dn consist of a multivalued RDN, we must build the dn in this case + // because the $entry_rdn is an array! + $filter_dn = Net_LDAP2_Util::canonical_dn($entry_rdn); + } + $base = Net_LDAP2_Util::canonical_dn($base); + + $result = @ldap_list($this->_link, $base, $entry_rdn, array(), 1, 1); + if (@ldap_count_entries($this->_link, $result)) { + return true; + } + if (ldap_errno($this->_link) == 32) { + return false; + } + if (ldap_errno($this->_link) != 0) { + return PEAR::raiseError(ldap_error($this->_link), ldap_errno($this->_link)); + } + return false; + } + + + /** + * Get a specific entry based on the DN + * + * @param string $dn DN of the entry that should be fetched + * @param array $attr Array of Attributes to select. If ommitted, all attributes are fetched. + * + * @return Net_LDAP2_Entry|Net_LDAP2_Error Reference to a Net_LDAP2_Entry object or Net_LDAP2_Error object + * @todo Maybe check against the shema should be done to be sure the attribute type exists + */ + public function &getEntry($dn, $attr = array()) + { + if (!is_array($attr)) { + $attr = array($attr); + } + $result = $this->search($dn, '(objectClass=*)', + array('scope' => 'base', 'attributes' => $attr)); + if (self::isError($result)) { + return $result; + } elseif ($result->count() == 0) { + return PEAR::raiseError('Could not fetch entry '.$dn.': no entry found'); + } + $entry = $result->shiftEntry(); + if (false == $entry) { + return PEAR::raiseError('Could not fetch entry (error retrieving entry from search result)'); + } + return $entry; + } + + /** + * Rename or move an entry + * + * This method will instantly carry out an update() after the move, + * so the entry is moved instantly. + * You can pass an optional Net_LDAP2 object. In this case, a cross directory + * move will be performed which deletes the entry in the source (THIS) directory + * and adds it in the directory $target_ldap. + * A cross directory move will switch the Entrys internal LDAP reference so + * updates to the entry will go to the new directory. + * + * Note that if you want to do a cross directory move, you need to + * pass an Net_LDAP2_Entry object, otherwise the attributes will be empty. + * + * @param string|Net_LDAP2_Entry $entry Entry DN or Entry object + * @param string $newdn New location + * @param Net_LDAP2 $target_ldap (optional) Target directory for cross server move; should be passed via reference + * + * @return Net_LDAP2_Error|true + */ + public function move($entry, $newdn, $target_ldap = null) + { + if (is_string($entry)) { + $entry_o = $this->getEntry($entry); + } else { + $entry_o =& $entry; + } + if (!$entry_o instanceof Net_LDAP2_Entry) { + return PEAR::raiseError('Parameter $entry is expected to be a Net_LDAP2_Entry object! (If DN was passed, conversion failed)'); + } + if (null !== $target_ldap && !$target_ldap instanceof Net_LDAP2) { + return PEAR::raiseError('Parameter $target_ldap is expected to be a Net_LDAP2 object!'); + } + + if ($target_ldap && $target_ldap !== $this) { + // cross directory move + if (is_string($entry)) { + return PEAR::raiseError('Unable to perform cross directory move: operation requires a Net_LDAP2_Entry object'); + } + if ($target_ldap->dnExists($newdn)) { + return PEAR::raiseError('Unable to perform cross directory move: entry does exist in target directory'); + } + $entry_o->dn($newdn); + $res = $target_ldap->add($entry_o); + if (self::isError($res)) { + return PEAR::raiseError('Unable to perform cross directory move: '.$res->getMessage().' in target directory'); + } + $res = $this->delete($entry_o->currentDN()); + if (self::isError($res)) { + $res2 = $target_ldap->delete($entry_o); // undo add + if (self::isError($res2)) { + $add_error_string = 'Additionally, the deletion (undo add) of $entry in target directory failed.'; + } + return PEAR::raiseError('Unable to perform cross directory move: '.$res->getMessage().' in source directory. '.$add_error_string); + } + $entry_o->setLDAP($target_ldap); + return true; + } else { + // local move + $entry_o->dn($newdn); + $entry_o->setLDAP($this); + return $entry_o->update(); + } + } + + /** + * Copy an entry to a new location + * + * The entry will be immediately copied. + * Please note that only attributes you have + * selected will be copied. + * + * @param Net_LDAP2_Entry &$entry Entry object + * @param string $newdn New FQF-DN of the entry + * + * @return Net_LDAP2_Error|Net_LDAP2_Entry Error Message or reference to the copied entry + */ + public function ©(&$entry, $newdn) + { + if (!$entry instanceof Net_LDAP2_Entry) { + return PEAR::raiseError('Parameter $entry is expected to be a Net_LDAP2_Entry object!'); + } + + $newentry = Net_LDAP2_Entry::createFresh($newdn, $entry->getValues()); + $result = $this->add($newentry); + + if ($result instanceof Net_LDAP2_Error) { + return $result; + } else { + return $newentry; + } + } + + + /** + * Returns the string for an ldap errorcode. + * + * Made to be able to make better errorhandling + * Function based on DB::errorMessage() + * Tip: The best description of the errorcodes is found here: + * http://www.directory-info.com/LDAP2/LDAPErrorCodes.html + * + * @param int $errorcode Error code + * + * @return string The errorstring for the error. + */ + public function errorMessage($errorcode) + { + $errorMessages = array( + 0x00 => "LDAP_SUCCESS", + 0x01 => "LDAP_OPERATIONS_ERROR", + 0x02 => "LDAP_PROTOCOL_ERROR", + 0x03 => "LDAP_TIMELIMIT_EXCEEDED", + 0x04 => "LDAP_SIZELIMIT_EXCEEDED", + 0x05 => "LDAP_COMPARE_FALSE", + 0x06 => "LDAP_COMPARE_TRUE", + 0x07 => "LDAP_AUTH_METHOD_NOT_SUPPORTED", + 0x08 => "LDAP_STRONG_AUTH_REQUIRED", + 0x09 => "LDAP_PARTIAL_RESULTS", + 0x0a => "LDAP_REFERRAL", + 0x0b => "LDAP_ADMINLIMIT_EXCEEDED", + 0x0c => "LDAP_UNAVAILABLE_CRITICAL_EXTENSION", + 0x0d => "LDAP_CONFIDENTIALITY_REQUIRED", + 0x0e => "LDAP_SASL_BIND_INPROGRESS", + 0x10 => "LDAP_NO_SUCH_ATTRIBUTE", + 0x11 => "LDAP_UNDEFINED_TYPE", + 0x12 => "LDAP_INAPPROPRIATE_MATCHING", + 0x13 => "LDAP_CONSTRAINT_VIOLATION", + 0x14 => "LDAP_TYPE_OR_VALUE_EXISTS", + 0x15 => "LDAP_INVALID_SYNTAX", + 0x20 => "LDAP_NO_SUCH_OBJECT", + 0x21 => "LDAP_ALIAS_PROBLEM", + 0x22 => "LDAP_INVALID_DN_SYNTAX", + 0x23 => "LDAP_IS_LEAF", + 0x24 => "LDAP_ALIAS_DEREF_PROBLEM", + 0x30 => "LDAP_INAPPROPRIATE_AUTH", + 0x31 => "LDAP_INVALID_CREDENTIALS", + 0x32 => "LDAP_INSUFFICIENT_ACCESS", + 0x33 => "LDAP_BUSY", + 0x34 => "LDAP_UNAVAILABLE", + 0x35 => "LDAP_UNWILLING_TO_PERFORM", + 0x36 => "LDAP_LOOP_DETECT", + 0x3C => "LDAP_SORT_CONTROL_MISSING", + 0x3D => "LDAP_INDEX_RANGE_ERROR", + 0x40 => "LDAP_NAMING_VIOLATION", + 0x41 => "LDAP_OBJECT_CLASS_VIOLATION", + 0x42 => "LDAP_NOT_ALLOWED_ON_NONLEAF", + 0x43 => "LDAP_NOT_ALLOWED_ON_RDN", + 0x44 => "LDAP_ALREADY_EXISTS", + 0x45 => "LDAP_NO_OBJECT_CLASS_MODS", + 0x46 => "LDAP_RESULTS_TOO_LARGE", + 0x47 => "LDAP_AFFECTS_MULTIPLE_DSAS", + 0x50 => "LDAP_OTHER", + 0x51 => "LDAP_SERVER_DOWN", + 0x52 => "LDAP_LOCAL_ERROR", + 0x53 => "LDAP_ENCODING_ERROR", + 0x54 => "LDAP_DECODING_ERROR", + 0x55 => "LDAP_TIMEOUT", + 0x56 => "LDAP_AUTH_UNKNOWN", + 0x57 => "LDAP_FILTER_ERROR", + 0x58 => "LDAP_USER_CANCELLED", + 0x59 => "LDAP_PARAM_ERROR", + 0x5a => "LDAP_NO_MEMORY", + 0x5b => "LDAP_CONNECT_ERROR", + 0x5c => "LDAP_NOT_SUPPORTED", + 0x5d => "LDAP_CONTROL_NOT_FOUND", + 0x5e => "LDAP_NO_RESULTS_RETURNED", + 0x5f => "LDAP_MORE_RESULTS_TO_RETURN", + 0x60 => "LDAP_CLIENT_LOOP", + 0x61 => "LDAP_REFERRAL_LIMIT_EXCEEDED", + 1000 => "Unknown Net_LDAP2 Error" + ); + + return isset($errorMessages[$errorcode]) ? + $errorMessages[$errorcode] : + $errorMessages[NET_LDAP2_ERROR] . ' (' . $errorcode . ')'; + } + + /** + * Gets a rootDSE object + * + * This either fetches a fresh rootDSE object or returns it from + * the internal cache for performance reasons, if possible. + * + * @param array $attrs Array of attributes to search for + * + * @access public + * @return Net_LDAP2_Error|Net_LDAP2_RootDSE Net_LDAP2_Error or Net_LDAP2_RootDSE object + */ + public function &rootDse($attrs = null) + { + if ($attrs !== null && !is_array($attrs)) { + return PEAR::raiseError('Parameter $attr is expected to be an array!'); + } + + $attrs_signature = serialize($attrs); + + // see if we need to fetch a fresh object, or if we already + // requested this object with the same attributes + if (true || !array_key_exists($attrs_signature, $this->_rootDSE_cache)) { + $rootdse =& Net_LDAP2_RootDSE::fetch($this, $attrs); + if ($rootdse instanceof Net_LDAP2_Error) { + return $rootdse; + } + + // search was ok, store rootDSE in cache + $this->_rootDSE_cache[$attrs_signature] = $rootdse; + } + return $this->_rootDSE_cache[$attrs_signature]; + } + + /** + * Alias function of rootDse() for perl-ldap interface + * + * @access public + * @see rootDse() + * @return Net_LDAP2_Error|Net_LDAP2_RootDSE + */ + public function &root_dse() + { + $args = func_get_args(); + return call_user_func_array(array(&$this, 'rootDse'), $args); + } + + /** + * Get a schema object + * + * @param string $dn (optional) Subschema entry dn + * + * @access public + * @return Net_LDAP2_Schema|Net_LDAP2_Error Net_LDAP2_Schema or Net_LDAP2_Error object + */ + public function &schema($dn = null) + { + // Schema caching by Knut-Olav Hoven + // If a schema caching object is registered, we use that to fetch + // a schema object. + // See registerSchemaCache() for more info on this. + if ($this->_schema === null) { + if ($this->_schema_cache) { + $cached_schema = $this->_schema_cache->loadSchema(); + if ($cached_schema instanceof Net_LDAP2_Error) { + return $cached_schema; // route error to client + } else { + if ($cached_schema instanceof Net_LDAP2_Schema) { + $this->_schema = $cached_schema; + } + } + } + } + + // Fetch schema, if not tried before and no cached version available. + // If we are already fetching the schema, we will skip fetching. + if ($this->_schema === null) { + // store a temporary error message so subsequent calls to schema() can + // detect, that we are fetching the schema already. + // Otherwise we will get an infinite loop at Net_LDAP2_Schema::fetch() + $this->_schema = new Net_LDAP2_Error('Schema not initialized'); + $this->_schema = Net_LDAP2_Schema::fetch($this, $dn); + + // If schema caching is active, advise the cache to store the schema + if ($this->_schema_cache) { + $caching_result = $this->_schema_cache->storeSchema($this->_schema); + if ($caching_result instanceof Net_LDAP2_Error) { + return $caching_result; // route error to client + } + } + } + return $this->_schema; + } + + /** + * Enable/disable persistent schema caching + * + * Sometimes it might be useful to allow your scripts to cache + * the schema information on disk, so the schema is not fetched + * every time the script runs which could make your scripts run + * faster. + * + * This method allows you to register a custom object that + * implements your schema cache. Please see the SchemaCache interface + * (SchemaCache.interface.php) for informations on how to implement this. + * To unregister the cache, pass null as $cache parameter. + * + * For ease of use, Net_LDAP2 provides a simple file based cache + * which is used in the example below. You may use this, for example, + * to store the schema in a linux tmpfs which results in the schema + * beeing cached inside the RAM which allows nearly instant access. + * + * // Create the simple file cache object that comes along with Net_LDAP2 + * $mySchemaCache_cfg = array( + * 'path' => '/tmp/Net_LDAP2_Schema.cache', + * 'max_age' => 86400 // max age is 24 hours (in seconds) + * ); + * $mySchemaCache = new Net_LDAP2_SimpleFileSchemaCache($mySchemaCache_cfg); + * $ldap = new Net_LDAP2::connect(...); + * $ldap->registerSchemaCache($mySchemaCache); // enable caching + * // now each call to $ldap->schema() will get the schema from disk! + * + * + * @param Net_LDAP2_SchemaCache|null $cache Object implementing the Net_LDAP2_SchemaCache interface + * + * @return true|Net_LDAP2_Error + */ + public function registerSchemaCache($cache) { + if (is_null($cache) + || (is_object($cache) && in_array('Net_LDAP2_SchemaCache', class_implements($cache))) ) { + $this->_schema_cache = $cache; + return true; + } else { + return new Net_LDAP2_Error('Custom schema caching object is either no '. + 'valid object or does not implement the Net_LDAP2_SchemaCache interface!'); + } + } + + + /** + * Checks if phps ldap-extension is loaded + * + * If it is not loaded, it tries to load it manually using PHPs dl(). + * It knows both windows-dll and *nix-so. + * + * @static + * @return Net_LDAP2_Error|true + */ + public static function checkLDAPExtension() + { + if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) { + return new Net_LDAP2_Error("It seems that you do not have the ldap-extension installed. Please install it before using the Net_LDAP2 package."); + } else { + return true; + } + } + + /** + * Encodes given attributes to UTF8 if needed by schema + * + * This function takes attributes in an array and then checks against the schema if they need + * UTF8 encoding. If that is so, they will be encoded. An encoded array will be returned and + * can be used for adding or modifying. + * + * $attributes is expected to be an array with keys describing + * the attribute names and the values as the value of this attribute: + * $attributes = array('cn' => 'foo', 'attr2' => array('mv1', 'mv2')); + * + * @param array $attributes Array of attributes + * + * @access public + * @return array|Net_LDAP2_Error Array of UTF8 encoded attributes or Error + */ + public function utf8Encode($attributes) + { + return $this->utf8($attributes, 'utf8_encode'); + } + + /** + * Decodes the given attribute values if needed by schema + * + * $attributes is expected to be an array with keys describing + * the attribute names and the values as the value of this attribute: + * $attributes = array('cn' => 'foo', 'attr2' => array('mv1', 'mv2')); + * + * @param array $attributes Array of attributes + * + * @access public + * @see utf8Encode() + * @return array|Net_LDAP2_Error Array with decoded attribute values or Error + */ + public function utf8Decode($attributes) + { + return $this->utf8($attributes, 'utf8_decode'); + } + + /** + * Encodes or decodes attribute values if needed + * + * @param array $attributes Array of attributes + * @param array $function Function to apply to attribute values + * + * @access protected + * @return array|Net_LDAP2_Error Array of attributes with function applied to values or Error + */ + protected function utf8($attributes, $function) + { + if (!is_array($attributes) || array_key_exists(0, $attributes)) { + return PEAR::raiseError('Parameter $attributes is expected to be an associative array'); + } + + if (!$this->_schema) { + $this->_schema = $this->schema(); + } + + if (!$this->_link || self::isError($this->_schema) || !function_exists($function)) { + return $attributes; + } + + if (is_array($attributes) && count($attributes) > 0) { + + foreach ($attributes as $k => $v) { + + if (!isset($this->_schemaAttrs[$k])) { + + $attr = $this->_schema->get('attribute', $k); + if (self::isError($attr)) { + continue; + } + + if (false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) { + $encode = true; + } else { + $encode = false; + } + $this->_schemaAttrs[$k] = $encode; + + } else { + $encode = $this->_schemaAttrs[$k]; + } + + if ($encode) { + if (is_array($v)) { + foreach ($v as $ak => $av) { + $v[$ak] = call_user_func($function, $av); + } + } else { + $v = call_user_func($function, $v); + } + } + $attributes[$k] = $v; + } + } + return $attributes; + } + + /** + * Get the LDAP link resource. It will loop attempting to + * re-establish the connection if the connection attempt fails and + * auto_reconnect has been turned on (see the _config array documentation). + * + * @access public + * @return resource LDAP link + */ + public function &getLink() + { + if ($this->_config['auto_reconnect']) { + while (true) { + // + // Return the link handle if we are already connected. Otherwise + // try to reconnect. + // + if ($this->_link !== false) { + return $this->_link; + } else { + $this->performReconnect(); + } + } + } + return $this->_link; + } +} + +/** +* Net_LDAP2_Error implements a class for reporting portable LDAP error messages. +* +* @category Net +* @package Net_LDAP2 +* @author Tarjej Huse +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP22/ +*/ +class Net_LDAP2_Error extends PEAR_Error +{ + /** + * Net_LDAP2_Error constructor. + * + * @param string $message String with error message. + * @param integer $code Net_LDAP2 error code + * @param integer $mode what "error mode" to operate in + * @param mixed $level what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query + * + * @access public + * @see PEAR_Error + */ + public function __construct($message = 'Net_LDAP2_Error', $code = NET_LDAP2_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) + { + if (is_int($code)) { + $this->PEAR_Error($message . ': ' . Net_LDAP2::errorMessage($code), $code, $mode, $level, $debuginfo); + } else { + $this->PEAR_Error("$message: $code", NET_LDAP2_ERROR, $mode, $level, $debuginfo); + } + } +} + +?> diff --git a/thirdparty/pear/Net/LDAP2/Entry.php b/thirdparty/pear/Net/LDAP2/Entry.php new file mode 100644 index 0000000..66de966 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/Entry.php @@ -0,0 +1,1055 @@ + +* @author Tarjej Huse +* @author Benedikt Hallinger +* @copyright 2009 Tarjej Huse, Jan Wagner, Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: Entry.php 286787 2009-08-04 06:03:12Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; +require_once 'Util.php'; + +/** +* Object representation of a directory entry +* +* This class represents a directory entry. You can add, delete, replace +* attributes and their values, rename the entry, delete the entry. +* +* @category Net +* @package Net_LDAP2 +* @author Jan Wagner +* @author Tarjej Huse +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP2/ +*/ +class Net_LDAP2_Entry extends PEAR +{ + /** + * Entry ressource identifier + * + * @access protected + * @var ressource + */ + protected $_entry = null; + + /** + * LDAP ressource identifier + * + * @access protected + * @var ressource + */ + protected $_link = null; + + /** + * Net_LDAP2 object + * + * This object will be used for updating and schema checking + * + * @access protected + * @var object Net_LDAP2 + */ + protected $_ldap = null; + + /** + * Distinguished name of the entry + * + * @access protected + * @var string + */ + protected $_dn = null; + + /** + * Attributes + * + * @access protected + * @var array + */ + protected $_attributes = array(); + + /** + * Original attributes before any modification + * + * @access protected + * @var array + */ + protected $_original = array(); + + + /** + * Map of attribute names + * + * @access protected + * @var array + */ + protected $_map = array(); + + + /** + * Is this a new entry? + * + * @access protected + * @var boolean + */ + protected $_new = true; + + /** + * New distinguished name + * + * @access protected + * @var string + */ + protected $_newdn = null; + + /** + * Shall the entry be deleted? + * + * @access protected + * @var boolean + */ + protected $_delete = false; + + /** + * Map with changes to the entry + * + * @access protected + * @var array + */ + protected $_changes = array("add" => array(), + "delete" => array(), + "replace" => array() + ); + /** + * Internal Constructor + * + * Constructor of the entry. Sets up the distinguished name and the entries + * attributes. + * You should not call this method manually! Use {@link Net_LDAP2_Entry::createFresh()} + * or {@link Net_LDAP2_Entry::createConnected()} instead! + * + * @param Net_LDAP2|ressource|array &$ldap Net_LDAP2 object, ldap-link ressource or array of attributes + * @param string|ressource $entry Either a DN or a LDAP-Entry ressource + * + * @access protected + * @return none + */ + protected function __construct(&$ldap, $entry = null) + { + $this->PEAR('Net_LDAP2_Error'); + + // set up entry resource or DN + if (is_resource($entry)) { + $this->_entry = &$entry; + } else { + $this->_dn = $entry; + } + + // set up LDAP link + if ($ldap instanceof Net_LDAP2) { + $this->_ldap = &$ldap; + $this->_link = $ldap->getLink(); + } elseif (is_resource($ldap)) { + $this->_link = $ldap; + } elseif (is_array($ldap)) { + // Special case: here $ldap is an array of attributes, + // this means, we have no link. This is a "virtual" entry. + // We just set up the attributes so one can work with the object + // as expected, but an update() fails unless setLDAP() is called. + $this->setAttributes($ldap); + } + + // if this is an entry existing in the directory, + // then set up as old and fetch attrs + if (is_resource($this->_entry) && is_resource($this->_link)) { + $this->_new = false; + $this->_dn = @ldap_get_dn($this->_link, $this->_entry); + $this->setAttributes(); // fetch attributes from server + } + } + + /** + * Creates a fresh entry that may be added to the directory later on + * + * Use this method, if you want to initialize a fresh entry. + * + * The method should be called statically: $entry = Net_LDAP2_Entry::createFresh(); + * You should put a 'objectClass' attribute into the $attrs so the directory server + * knows which object you want to create. However, you may omit this in case you + * don't want to add this entry to a directory server. + * + * The attributes parameter is as following: + * + * $attrs = array( 'attribute1' => array('value1', 'value2'), + * 'attribute2' => 'single value' + * ); + * + * + * @param string $dn DN of the Entry + * @param array $attrs Attributes of the entry + * + * @static + * @return Net_LDAP2_Entry|Net_LDAP2_Error + */ + public static function createFresh($dn, $attrs = array()) + { + if (!is_array($attrs)) { + return PEAR::raiseError("Unable to create fresh entry: Parameter \$attrs needs to be an array!"); + } + + $entry = new Net_LDAP2_Entry($attrs, $dn); + return $entry; + } + + /** + * Creates a Net_LDAP2_Entry object out of an ldap entry resource + * + * Use this method, if you want to initialize an entry object that is + * already present in some directory and that you have read manually. + * + * Please note, that if you want to create an entry object that represents + * some already existing entry, you should use {@link createExisting()}. + * + * The method should be called statically: $entry = Net_LDAP2_Entry::createConnected(); + * + * @param Net_LDAP2 $ldap Net_LDA2 object + * @param resource $entry PHP LDAP entry resource + * + * @static + * @return Net_LDAP2_Entry|Net_LDAP2_Error + */ + public static function createConnected($ldap, $entry) + { + if (!$ldap instanceof Net_LDAP2) { + return PEAR::raiseError("Unable to create connected entry: Parameter \$ldap needs to be a Net_LDAP2 object!"); + } + if (!is_resource($entry)) { + return PEAR::raiseError("Unable to create connected entry: Parameter \$entry needs to be a ldap entry resource!"); + } + + $entry = new Net_LDAP2_Entry($ldap, $entry); + return $entry; + } + + /** + * Creates an Net_LDAP2_Entry object that is considered already existing + * + * Use this method, if you want to modify an already existing entry + * without fetching it first. + * In most cases however, it is better to fetch the entry via Net_LDAP2->getEntry()! + * + * Please note that you should take care if you construct entries manually with this + * because you may get weird synchronisation problems. + * The attributes and values as well as the entry itself are considered existent + * which may produce errors if you try to modify an entry which doesn't really exist + * or if you try to overwrite some attribute with an value already present. + * + * This method is equal to calling createFresh() and after that markAsNew(FALSE). + * + * The method should be called statically: $entry = Net_LDAP2_Entry::createExisting(); + * + * The attributes parameter is as following: + * + * $attrs = array( 'attribute1' => array('value1', 'value2'), + * 'attribute2' => 'single value' + * ); + * + * + * @param string $dn DN of the Entry + * @param array $attrs Attributes of the entry + * + * @static + * @return Net_LDAP2_Entry|Net_LDAP2_Error + */ + public static function createExisting($dn, $attrs = array()) + { + if (!is_array($attrs)) { + return PEAR::raiseError("Unable to create entry object: Parameter \$attrs needs to be an array!"); + } + + $entry = Net_LDAP2_Entry::createFresh($dn, $attrs); + if ($entry instanceof Net_LDAP2_Error) { + return $entry; + } else { + $entry->markAsNew(false); + return $entry; + } + } + + /** + * Get or set the distinguished name of the entry + * + * If called without an argument the current (or the new DN if set) DN gets returned. + * If you provide an DN, this entry is moved to the new location specified if a DN existed. + * If the DN was not set, the DN gets initialized. Call {@link update()} to actually create + * the new Entry in the directory. + * To fetch the current active DN after setting a new DN but before an update(), you can use + * {@link currentDN()} to retrieve the DN that is currently active. + * + * Please note that special characters (eg german umlauts) should be encoded using utf8_encode(). + * You may use {@link Net_LDAP2_Util::canonical_dn()} for properly encoding of the DN. + * + * @param string $dn New distinguished name + * + * @access public + * @return string|true Distinguished name (or true if a new DN was provided) + */ + public function dn($dn = null) + { + if (false == is_null($dn)) { + if (is_null($this->_dn)) { + $this->_dn = $dn; + } else { + $this->_newdn = $dn; + } + return true; + } + return (isset($this->_newdn) ? $this->_newdn : $this->currentDN()); + } + + /** + * Renames or moves the entry + * + * This is just a convinience alias to {@link dn()} + * to make your code more meaningful. + * + * @param string $newdn The new DN + * + * @return true + */ + public function move($newdn) + { + return $this->dn($newdn); + } + + /** + * Sets the internal attributes array + * + * This fetches the values for the attributes from the server. + * The attribute Syntax will be checked so binary attributes will be returned + * as binary values. + * + * Attributes may be passed directly via the $attributes parameter to setup this + * entry manually. This overrides attribute fetching from the server. + * + * @param array $attributes Attributes to set for this entry + * + * @access protected + * @return void + */ + protected function setAttributes($attributes = null) + { + /* + * fetch attributes from the server + */ + if (is_null($attributes) && is_resource($this->_entry) && is_resource($this->_link)) { + // fetch schema + if ($this->_ldap instanceof Net_LDAP2) { + $schema =& $this->_ldap->schema(); + } + // fetch attributes + $attributes = array(); + do { + if (empty($attr)) { + $ber = null; + $attr = @ldap_first_attribute($this->_link, $this->_entry, $ber); + } else { + $attr = @ldap_next_attribute($this->_link, $this->_entry, $ber); + } + if ($attr) { + $func = 'ldap_get_values'; // standard function to fetch value + + // Try to get binary values as binary data + if ($schema instanceof Net_LDAP2_Schema) { + if ($schema->isBinary($attr)) { + $func = 'ldap_get_values_len'; + } + } + // fetch attribute value (needs error checking?) + $attributes[$attr] = $func($this->_link, $this->_entry, $attr); + } + } while ($attr); + } + + /* + * set attribute data directly, if passed + */ + if (is_array($attributes) && count($attributes) > 0) { + if (isset($attributes["count"]) && is_numeric($attributes["count"])) { + unset($attributes["count"]); + } + foreach ($attributes as $k => $v) { + // attribute names should not be numeric + if (is_numeric($k)) { + continue; + } + // map generic attribute name to real one + $this->_map[strtolower($k)] = $k; + // attribute values should be in an array + if (false == is_array($v)) { + $v = array($v); + } + // remove the value count (comes from ldap server) + if (isset($v["count"])) { + unset($v["count"]); + } + $this->_attributes[$k] = $v; + } + } + + // save a copy for later use + $this->_original = $this->_attributes; + } + + /** + * Get the values of all attributes in a hash + * + * The returned hash has the form + * array('attributename' => 'single value', + * 'attributename' => array('value1', value2', value3')) + * + * @access public + * @return array Hash of all attributes with their values + */ + public function getValues() + { + $attrs = array(); + foreach ($this->_attributes as $attr => $value) { + $attrs[$attr] = $this->getValue($attr); + } + return $attrs; + } + + /** + * Get the value of a specific attribute + * + * The first parameter is the name of the attribute + * The second parameter influences the way the value is returned: + * 'single': only the first value is returned as string + * 'all': all values including the value count are returned in an + * array + * 'default': in all other cases an attribute value with a single value is + * returned as string, if it has multiple values it is returned + * as an array (without value count) + * + * @param string $attr Attribute name + * @param string $option Option + * + * @access public + * @return string|array|PEAR_Error string, array or PEAR_Error + */ + public function getValue($attr, $option = null) + { + $attr = $this->getAttrName($attr); + + if (false == array_key_exists($attr, $this->_attributes)) { + return PEAR::raiseError("Unknown attribute ($attr) requested"); + } + + $value = $this->_attributes[$attr]; + + if ($option == "single" || (count($value) == 1 && $option != 'all')) { + $value = array_shift($value); + } + + return $value; + } + + /** + * Alias function of getValue for perl-ldap interface + * + * @see getValue() + * @return string|array|PEAR_Error + */ + public function get_value() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'getValue' ), $args); + } + + /** + * Returns an array of attributes names + * + * @access public + * @return array Array of attribute names + */ + public function attributes() + { + return array_keys($this->_attributes); + } + + /** + * Returns whether an attribute exists or not + * + * @param string $attr Attribute name + * + * @access public + * @return boolean + */ + public function exists($attr) + { + $attr = $this->getAttrName($attr); + return array_key_exists($attr, $this->_attributes); + } + + /** + * Adds a new attribute or a new value to an existing attribute + * + * The paramter has to be an array of the form: + * array('attributename' => 'single value', + * 'attributename' => array('value1', 'value2)) + * When the attribute already exists the values will be added, else the + * attribute will be created. These changes are local to the entry and do + * not affect the entry on the server until update() is called. + * + * Note, that you can add values of attributes that you haven't selected, but if + * you do so, {@link getValue()} and {@link getValues()} will only return the + * values you added, _NOT_ all values present on the server. To avoid this, just refetch + * the entry after calling {@link update()} or select the attribute. + * + * @param array $attr Attributes to add + * + * @access public + * @return true|Net_LDAP2_Error + */ + public function add($attr = array()) + { + if (false == is_array($attr)) { + return PEAR::raiseError("Parameter must be an array"); + } + foreach ($attr as $k => $v) { + $k = $this->getAttrName($k); + if (false == is_array($v)) { + // Do not add empty values + if ($v == null) { + continue; + } else { + $v = array($v); + } + } + // add new values to existing attribute or add new attribute + if ($this->exists($k)) { + $this->_attributes[$k] = array_unique(array_merge($this->_attributes[$k], $v)); + } else { + $this->_map[strtolower($k)] = $k; + $this->_attributes[$k] = $v; + } + // save changes for update() + if (empty($this->_changes["add"][$k])) { + $this->_changes["add"][$k] = array(); + } + $this->_changes["add"][$k] = array_unique(array_merge($this->_changes["add"][$k], $v)); + } + $return = true; + return $return; + } + + /** + * Deletes an whole attribute or a value or the whole entry + * + * The parameter can be one of the following: + * + * "attributename" - The attribute as a whole will be deleted + * array("attributename1", "attributename2) - All given attributes will be + * deleted + * array("attributename" => "value") - The value will be deleted + * array("attributename" => array("value1", "value2") - The given values + * will be deleted + * If $attr is null or omitted , then the whole Entry will be deleted! + * + * These changes are local to the entry and do + * not affect the entry on the server until {@link update()} is called. + * + * Please note that you must select the attribute (at $ldap->search() for example) + * to be able to delete values of it, Otherwise {@link update()} will silently fail + * and remove nothing. + * + * @param string|array $attr Attributes to delete (NULL or missing to delete whole entry) + * + * @access public + * @return true + */ + public function delete($attr = null) + { + if (is_null($attr)) { + $this->_delete = true; + return true; + } + if (is_string($attr)) { + $attr = array($attr); + } + // Make the assumption that attribute names cannot be numeric, + // therefore this has to be a simple list of attribute names to delete + if (is_numeric(key($attr))) { + foreach ($attr as $name) { + if (is_array($name)) { + // someone mixed modes (list mode but specific values given!) + $del_attr_name = array_search($name, $attr); + $this->delete(array($del_attr_name => $name)); + } else { + // mark for update() if this attr was not marked before + $name = $this->getAttrName($name); + if ($this->exists($name)) { + $this->_changes["delete"][$name] = null; + unset($this->_attributes[$name]); + } + } + } + } else { + // Here we have a hash with "attributename" => "value to delete" + foreach ($attr as $name => $values) { + if (is_int($name)) { + // someone mixed modes and gave us just an attribute name + $this->delete($values); + } else { + // mark for update() if this attr was not marked before; + // this time it must consider the selected values also + $name = $this->getAttrName($name); + if ($this->exists($name)) { + if (false == is_array($values)) { + $values = array($values); + } + // save values to be deleted + if (empty($this->_changes["delete"][$name])) { + $this->_changes["delete"][$name] = array(); + } + $this->_changes["delete"][$name] = + array_unique(array_merge($this->_changes["delete"][$name], $values)); + foreach ($values as $value) { + // find the key for the value that should be deleted + $key = array_search($value, $this->_attributes[$name]); + if (false !== $key) { + // delete the value + unset($this->_attributes[$name][$key]); + } + } + } + } + } + } + $return = true; + return $return; + } + + /** + * Replaces attributes or its values + * + * The parameter has to an array of the following form: + * array("attributename" => "single value", + * "attribute2name" => array("value1", "value2"), + * "deleteme1" => null, + * "deleteme2" => "") + * If the attribute does not yet exist it will be added instead (see also $force). + * If the attribue value is null, the attribute will de deleted. + * + * These changes are local to the entry and do + * not affect the entry on the server until {@link update()} is called. + * + * In some cases you are not allowed to read the attributes value (for + * example the ActiveDirectory attribute unicodePwd) but are allowed to + * replace the value. In this case replace() would assume that the attribute + * is not in the directory yet and tries to add it which will result in an + * LDAP_TYPE_OR_VALUE_EXISTS error. + * To force replace mode instead of add, you can set $force to true. + * + * @param array $attr Attributes to replace + * @param bool $force Force replacing mode in case we can't read the attr value but are allowed to replace it + * + * @access public + * @return true|Net_LDAP2_Error + */ + public function replace($attr = array(), $force = false) + { + if (false == is_array($attr)) { + return PEAR::raiseError("Parameter must be an array"); + } + foreach ($attr as $k => $v) { + $k = $this->getAttrName($k); + if (false == is_array($v)) { + // delete attributes with empty values; treat ints as string + if (is_int($v)) { + $v = "$v"; + } + if ($v == null) { + $this->delete($k); + continue; + } else { + $v = array($v); + } + } + // existing attributes will get replaced + if ($this->exists($k) || $force) { + $this->_changes["replace"][$k] = $v; + $this->_attributes[$k] = $v; + } else { + // new ones just get added + $this->add(array($k => $v)); + } + } + $return = true; + return $return; + } + + /** + * Update the entry on the directory server + * + * This will evaluate all changes made so far and send them + * to the directory server. + * Please note, that if you make changes to objectclasses wich + * have mandatory attributes set, update() will currently fail. + * Remove the entry from the server and readd it as new in such cases. + * This also will deal with problems with setting structural object classes. + * + * @param Net_LDAP2 $ldap If passed, a call to setLDAP() is issued prior update, thus switching the LDAP-server. This is for perl-ldap interface compliance + * + * @access public + * @return true|Net_LDAP2_Error + * @todo Entry rename with a DN containing special characters needs testing! + */ + public function update($ldap = null) + { + if ($ldap) { + $msg = $this->setLDAP($ldap); + if (Net_LDAP2::isError($msg)) { + return PEAR::raiseError('You passed an invalid $ldap variable to update()'); + } + } + + // ensure we have a valid LDAP object + $ldap =& $this->getLDAP(); + if (!$ldap instanceof Net_LDAP2) { + return PEAR::raiseError("The entries LDAP object is not valid"); + } + + // Get and check link + $link = $ldap->getLink(); + if (!is_resource($link)) { + return PEAR::raiseError("Could not update entry: internal LDAP link is invalid"); + } + + /* + * Delete the entry + */ + if (true === $this->_delete) { + return $ldap->delete($this); + } + + /* + * New entry + */ + if (true === $this->_new) { + $msg = $ldap->add($this); + if (Net_LDAP2::isError($msg)) { + return $msg; + } + $this->_new = false; + $this->_changes['add'] = array(); + $this->_changes['delete'] = array(); + $this->_changes['replace'] = array(); + $this->_original = $this->_attributes; + + $return = true; + return $return; + } + + /* + * Rename/move entry + */ + if (false == is_null($this->_newdn)) { + if ($ldap->getLDAPVersion() !== 3) { + return PEAR::raiseError("Renaming/Moving an entry is only supported in LDAPv3"); + } + // make dn relative to parent (needed for ldap rename) + $parent = Net_LDAP2_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false)); + if (Net_LDAP2::isError($parent)) { + return $parent; + } + $child = array_shift($parent); + // maybe the dn consist of a multivalued RDN, we must build the dn in this case + // because the $child-RDN is an array! + if (is_array($child)) { + $child = Net_LDAP2_Util::canonical_dn($child); + } + $parent = Net_LDAP2_Util::canonical_dn($parent); + + // rename/move + if (false == @ldap_rename($link, $this->_dn, $child, $parent, true)) { + return PEAR::raiseError("Entry not renamed: " . + @ldap_error($link), @ldap_errno($link)); + } + // reflect changes to local copy + $this->_dn = $this->_newdn; + $this->_newdn = null; + } + + /* + * Carry out modifications to the entry + */ + // ADD + foreach ($this->_changes["add"] as $attr => $value) { + // if attribute exists, add new values + if ($this->exists($attr)) { + if (false === @ldap_mod_add($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not add new values to attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + } else { + // new attribute + if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not add new attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + } + // all went well here, I guess + unset($this->_changes["add"][$attr]); + } + + // DELETE + foreach ($this->_changes["delete"] as $attr => $value) { + // In LDAPv3 you need to specify the old values for deleting + if (is_null($value) && $ldap->getLDAPVersion() === 3) { + $value = $this->_original[$attr]; + } + if (false === @ldap_mod_del($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not delete attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + unset($this->_changes["delete"][$attr]); + } + + // REPLACE + foreach ($this->_changes["replace"] as $attr => $value) { + if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not replace attribute $attr values: " . + @ldap_error($link), @ldap_errno($link)); + } + unset($this->_changes["replace"][$attr]); + } + + // all went well, so _original (server) becomes _attributes (local copy) + $this->_original = $this->_attributes; + + $return = true; + return $return; + } + + /** + * Returns the right attribute name + * + * @param string $attr Name of attribute + * + * @access protected + * @return string The right name of the attribute + */ + protected function getAttrName($attr) + { + $name = strtolower($attr); + if (array_key_exists($name, $this->_map)) { + $attr = $this->_map[$name]; + } + return $attr; + } + + /** + * Returns a reference to the LDAP-Object of this entry + * + * @access public + * @return Net_LDAP2|Net_LDAP2_Error Reference to the Net_LDAP2 Object (the connection) or Net_LDAP2_Error + */ + public function &getLDAP() + { + if (!$this->_ldap instanceof Net_LDAP2) { + $err = new PEAR_Error('LDAP is not a valid Net_LDAP2 object'); + return $err; + } else { + return $this->_ldap; + } + } + + /** + * Sets a reference to the LDAP-Object of this entry + * + * After setting a Net_LDAP2 object, calling update() will use that object for + * updating directory contents. Use this to dynamicly switch directorys. + * + * @param Net_LDAP2 &$ldap Net_LDAP2 object that this entry should be connected to + * + * @access public + * @return true|Net_LDAP2_Error + */ + public function setLDAP(&$ldap) + { + if (!$ldap instanceof Net_LDAP2) { + return PEAR::raiseError("LDAP is not a valid Net_LDAP2 object"); + } else { + $this->_ldap =& $ldap; + return true; + } + } + + /** + * Marks the entry as new/existing. + * + * If an Entry is marked as new, it will be added to the directory + * when calling {@link update()}. + * If the entry is marked as old ($mark = false), then the entry is + * assumed to be present in the directory server wich results in + * modification when calling {@link update()}. + * + * @param boolean $mark Value to set, defaults to "true" + * + * @return void + */ + public function markAsNew($mark = true) + { + $this->_new = ($mark)? true : false; + } + + /** + * Applies a regular expression onto a single- or multivalued attribute (like preg_match()) + * + * This method behaves like PHPs preg_match() but with some exceptions. + * If you want to retrieve match information, then you MUST pass the + * $matches parameter via reference! otherwise you will get no matches. + * Since it is possible to have multi valued attributes the $matches + * array will have a additionally numerical dimension (one for each value): + * + * $matches = array( + * 0 => array (usual preg_match() returnarray), + * 1 => array (usual preg_match() returnarray) + * ) + * + * Please note, that $matches will be initialized to an empty array inside. + * + * Usage example: + * + * $result = $entry->preg_match('/089(\d+)/', 'telephoneNumber', &$matches); + * if ( $result === true ){ + * echo "First match: ".$matches[0][1]; // Match of value 1, content of first bracket + * } else { + * if ( Net_LDAP2::isError($result) ) { + * echo "Error: ".$result->getMessage(); + * } else { + * echo "No match found."; + * } + * } + * + * + * Please note that it is important to test for an Net_LDAP2_Error, because objects are + * evaluating to true by default, thus if an error occured, and you only check using "==" then + * you get misleading results. Use the "identical" (===) operator to test for matches to + * avoid this as shown above. + * + * @param string $regex The regular expression + * @param string $attr_name The attribute to search in + * @param array $matches (optional, PASS BY REFERENCE!) Array to store matches in + * + * @return boolean|Net_LDAP2_Error TRUE, if we had a match in one of the values, otherwise false. Net_LDAP2_Error in case something went wrong + */ + public function pregMatch($regex, $attr_name, $matches = array()) + { + $matches = array(); + + // fetch attribute values + $attr = $this->getValue($attr_name, 'all'); + if (Net_LDAP2::isError($attr)) { + return $attr; + } else { + unset($attr['count']); + } + + // perform preg_match() on all values + $match = false; + foreach ($attr as $thisvalue) { + $matches_int = array(); + if (preg_match($regex, $thisvalue, $matches_int)) { + $match = true; + array_push($matches, $matches_int); // store matches in reference + } + } + return $match; + } + + /** + * Alias of {@link pregMatch()} for compatibility to Net_LDAP 1 + * + * @see pregMatch() + * @return boolean|Net_LDAP2_Error + */ + public function preg_match() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'pregMatch' ), $args); + } + + /** + * Tells if the entry is consiedered as new (not present in the server) + * + * Please note, that this doesn't tell you if the entry is present on the server. + * Use {@link Net_LDAP2::dnExists()} to see if an entry is already there. + * + * @return boolean + */ + public function isNew() + { + return $this->_new; + } + + + /** + * Is this entry going to be deleted once update() is called? + * + * @return boolean + */ + public function willBeDeleted() + { + return $this->_delete; + } + + /** + * Is this entry going to be moved once update() is called? + * + * @return boolean + */ + public function willBeMoved() + { + return ($this->dn() !== $this->currentDN()); + } + + /** + * Returns always the original DN + * + * If an entry will be moved but {@link update()} was not called, + * {@link dn()} will return the new DN. This method however, returns + * always the current active DN. + * + * @return string + */ + public function currentDN() + { + return $this->_dn; + } + + /** + * Returns the attribute changes to be carried out once update() is called + * + * @return array + */ + public function getChanges() + { + return $this->_changes; + } +} +?> diff --git a/thirdparty/pear/Net/LDAP2/Filter.php b/thirdparty/pear/Net/LDAP2/Filter.php new file mode 100644 index 0000000..0723eda --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/Filter.php @@ -0,0 +1,514 @@ + +* @copyright 2009 Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: Filter.php 289978 2009-10-27 09:56:41Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; +require_once 'Util.php'; + +/** +* Object representation of a part of a LDAP filter. +* +* This Class is not completely compatible to the PERL interface! +* +* The purpose of this class is, that users can easily build LDAP filters +* without having to worry about right escaping etc. +* A Filter is built using several independent filter objects +* which are combined afterwards. This object works in two +* modes, depending how the object is created. +* If the object is created using the {@link create()} method, then this is a leaf-object. +* If the object is created using the {@link combine()} method, then this is a container object. +* +* LDAP filters are defined in RFC-2254 and can be found under +* {@link http://www.ietf.org/rfc/rfc2254.txt} +* +* Here a quick copy&paste example: +* +* $filter0 = Net_LDAP2_Filter::create('stars', 'equals', '***'); +* $filter_not0 = Net_LDAP2_Filter::combine('not', $filter0); +* +* $filter1 = Net_LDAP2_Filter::create('gn', 'begins', 'bar'); +* $filter2 = Net_LDAP2_Filter::create('gn', 'ends', 'baz'); +* $filter_comp = Net_LDAP2_Filter::combine('or',array($filter_not0, $filter1, $filter2)); +* +* echo $filter_comp->asString(); +* // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar*)(gn=*baz)) +* // The stars in $filter0 are treaten as real stars unless you disable escaping. +* +* +* @category Net +* @package Net_LDAP2 +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP2/ +*/ +class Net_LDAP2_Filter extends PEAR +{ + /** + * Storage for combination of filters + * + * This variable holds a array of filter objects + * that should be combined by this filter object. + * + * @access protected + * @var array + */ + protected $_subfilters = array(); + + /** + * Match of this filter + * + * If this is a leaf filter, then a matching rule is stored, + * if it is a container, then it is a logical operator + * + * @access protected + * @var string + */ + protected $_match; + + /** + * Single filter + * + * If we operate in leaf filter mode, + * then the constructing method stores + * the filter representation here + * + * @acces private + * @var string + */ + protected $_filter; + + /** + * Create a new Net_LDAP2_Filter object and parse $filter. + * + * This is for PERL Net::LDAP interface. + * Construction of Net_LDAP2_Filter objects should happen through either + * {@link create()} or {@link combine()} which give you more control. + * However, you may use the perl iterface if you already have generated filters. + * + * @param string $filter LDAP filter string + * + * @see parse() + */ + public function __construct($filter = false) + { + // The optional parameter must remain here, because otherwise create() crashes + if (false !== $filter) { + $filter_o = self::parse($filter); + if (PEAR::isError($filter_o)) { + $this->_filter = $filter_o; // assign error, so asString() can report it + } else { + $this->_filter = $filter_o->asString(); + } + } + } + + /** + * Constructor of a new part of a LDAP filter. + * + * The following matching rules exists: + * - equals: One of the attributes values is exactly $value + * Please note that case sensitiviness is depends on the + * attributes syntax configured in the server. + * - begins: One of the attributes values must begin with $value + * - ends: One of the attributes values must end with $value + * - contains: One of the attributes values must contain $value + * - present | any: The attribute can contain any value but must be existent + * - greater: The attributes value is greater than $value + * - less: The attributes value is less than $value + * - greaterOrEqual: The attributes value is greater or equal than $value + * - lessOrEqual: The attributes value is less or equal than $value + * - approx: One of the attributes values is similar to $value + * + * If $escape is set to true (default) then $value will be escaped + * properly. If it is set to false then $value will be treaten as raw filter value string. + * You should escape yourself using {@link Net_LDAP2_Util::escape_filter_value()}! + * + * Examples: + * + * // This will find entries that contain an attribute "sn" that ends with "foobar": + * $filter = new Net_LDAP2_Filter('sn', 'ends', 'foobar'); + * + * // This will find entries that contain an attribute "sn" that has any value set: + * $filter = new Net_LDAP2_Filter('sn', 'any'); + * + * + * @param string $attr_name Name of the attribute the filter should apply to + * @param string $match Matching rule (equals, begins, ends, contains, greater, less, greaterOrEqual, lessOrEqual, approx, any) + * @param string $value (optional) if given, then this is used as a filter + * @param boolean $escape Should $value be escaped? (default: yes, see {@link Net_LDAP2_Util::escape_filter_value()} for detailed information) + * + * @return Net_LDAP2_Filter|Net_LDAP2_Error + */ + public static function &create($attr_name, $match, $value = '', $escape = true) + { + $leaf_filter = new Net_LDAP2_Filter(); + if ($escape) { + $array = Net_LDAP2_Util::escape_filter_value(array($value)); + $value = $array[0]; + } + switch (strtolower($match)) { + case 'equals': + $leaf_filter->_filter = '(' . $attr_name . '=' . $value . ')'; + break; + case 'begins': + $leaf_filter->_filter = '(' . $attr_name . '=' . $value . '*)'; + break; + case 'ends': + $leaf_filter->_filter = '(' . $attr_name . '=*' . $value . ')'; + break; + case 'contains': + $leaf_filter->_filter = '(' . $attr_name . '=*' . $value . '*)'; + break; + case 'greater': + $leaf_filter->_filter = '(' . $attr_name . '>' . $value . ')'; + break; + case 'less': + $leaf_filter->_filter = '(' . $attr_name . '<' . $value . ')'; + break; + case 'greaterorequal': + case '>=': + $leaf_filter->_filter = '(' . $attr_name . '>=' . $value . ')'; + break; + case 'lessorequal': + case '<=': + $leaf_filter->_filter = '(' . $attr_name . '<=' . $value . ')'; + break; + case 'approx': + case '~=': + $leaf_filter->_filter = '(' . $attr_name . '~=' . $value . ')'; + break; + case 'any': + case 'present': // alias that may improve user code readability + $leaf_filter->_filter = '(' . $attr_name . '=*)'; + break; + default: + return PEAR::raiseError('Net_LDAP2_Filter create error: matching rule "' . $match . '" not known!'); + } + return $leaf_filter; + } + + /** + * Combine two or more filter objects using a logical operator + * + * This static method combines two or more filter objects and returns one single + * filter object that contains all the others. + * Call this method statically: $filter = Net_LDAP2_Filter('or', array($filter1, $filter2)) + * If the array contains filter strings instead of filter objects, we will try to parse them. + * + * @param string $log_op The locicall operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!" + * @param array|Net_LDAP2_Filter $filters array with Net_LDAP2_Filter objects + * + * @return Net_LDAP2_Filter|Net_LDAP2_Error + * @static + */ + public static function &combine($log_op, $filters) + { + if (PEAR::isError($filters)) { + return $filters; + } + + // substitude named operators to logical operators + if ($log_op == 'and') $log_op = '&'; + if ($log_op == 'or') $log_op = '|'; + if ($log_op == 'not') $log_op = '!'; + + // tests for sane operation + if ($log_op == '!') { + // Not-combination, here we only accept one filter object or filter string + if ($filters instanceof Net_LDAP2_Filter) { + $filters = array($filters); // force array + } elseif (is_string($filters)) { + $filter_o = self::parse($filters); + if (PEAR::isError($filter_o)) { + $err = PEAR::raiseError('Net_LDAP2_Filter combine error: '.$filter_o->getMessage()); + return $err; + } else { + $filters = array($filter_o); + } + } elseif (is_array($filters)) { + $err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is an array!'); + return $err; + } else { + $err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is not a valid Net_LDAP2_Filter nor a filter string!'); + return $err; + } + } elseif ($log_op == '&' || $log_op == '|') { + if (!is_array($filters) || count($filters) < 2) { + $err = PEAR::raiseError('Net_LDAP2_Filter combine error: parameter $filters is not an array or contains less than two Net_LDAP2_Filter objects!'); + return $err; + } + } else { + $err = PEAR::raiseError('Net_LDAP2_Filter combine error: logical operator is not known!'); + return $err; + } + + $combined_filter = new Net_LDAP2_Filter(); + foreach ($filters as $key => $testfilter) { // check for errors + if (PEAR::isError($testfilter)) { + return $testfilter; + } elseif (is_string($testfilter)) { + // string found, try to parse into an filter object + $filter_o = self::parse($testfilter); + if (PEAR::isError($filter_o)) { + return $filter_o; + } else { + $filters[$key] = $filter_o; + } + } elseif (!$testfilter instanceof Net_LDAP2_Filter) { + $err = PEAR::raiseError('Net_LDAP2_Filter combine error: invalid object passed in array $filters!'); + return $err; + } + } + + $combined_filter->_subfilters = $filters; + $combined_filter->_match = $log_op; + return $combined_filter; + } + + /** + * Parse FILTER into a Net_LDAP2_Filter object + * + * This parses an filter string into Net_LDAP2_Filter objects. + * + * @param string $FILTER The filter string + * + * @access static + * @return Net_LDAP2_Filter|Net_LDAP2_Error + * @todo Leaf-mode: Do we need to escape at all? what about *-chars?check for the need of encoding values, tackle problems (see code comments) + */ + public static function parse($FILTER) + { + if (preg_match('/^\((.+?)\)$/', $FILTER, $matches)) { + if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) { + // Subfilter processing: pass subfilters to parse() and combine + // the objects using the logical operator detected + // we have now something like "&(...)(...)(...)" but at least one part ("!(...)"). + // Each subfilter could be an arbitary complex subfilter. + + // extract logical operator and filter arguments + $log_op = substr($matches[1], 0, 1); + $remaining_component = substr($matches[1], 1); + + // split $remaining_component into individual subfilters + // we cannot use split() for this, because we do not know the + // complexiness of the subfilter. Thus, we look trough the filter + // string and just recognize ending filters at the first level. + // We record the index number of the char and use that information + // later to split the string. + $sub_index_pos = array(); + $prev_char = ''; // previous character looked at + $level = 0; // denotes the current bracket level we are, + // >1 is too deep, 1 is ok, 0 is outside any + // subcomponent + for ($curpos = 0; $curpos < strlen($remaining_component); $curpos++) { + $cur_char = substr($remaining_component, $curpos, 1); + + // rise/lower bracket level + if ($cur_char == '(' && $prev_char != '\\') { + $level++; + } elseif ($cur_char == ')' && $prev_char != '\\') { + $level--; + } + + if ($cur_char == '(' && $prev_char == ')' && $level == 1) { + array_push($sub_index_pos, $curpos); // mark the position for splitting + } + $prev_char = $cur_char; + } + + // now perform the splits. To get also the last part, we + // need to add the "END" index to the split array + array_push($sub_index_pos, strlen($remaining_component)); + $subfilters = array(); + $oldpos = 0; + foreach ($sub_index_pos as $s_pos) { + $str_part = substr($remaining_component, $oldpos, $s_pos - $oldpos); + array_push($subfilters, $str_part); + $oldpos = $s_pos; + } + + // some error checking... + if (count($subfilters) == 1) { + // only one subfilter found + } elseif (count($subfilters) > 1) { + // several subfilters found + if ($log_op == "!") { + return PEAR::raiseError("Filter parsing error: invalid filter syntax - NOT operator detected but several arguments given!"); + } + } else { + // this should not happen unless the user specified a wrong filter + return PEAR::raiseError("Filter parsing error: invalid filter syntax - got operator '$log_op' but no argument!"); + } + + // Now parse the subfilters into objects and combine them using the operator + $subfilters_o = array(); + foreach ($subfilters as $s_s) { + $o = self::parse($s_s); + if (PEAR::isError($o)) { + return $o; + } else { + array_push($subfilters_o, self::parse($s_s)); + } + } + + $filter_o = self::combine($log_op, $subfilters_o); + return $filter_o; + + } else { + // This is one leaf filter component, do some syntax checks, then escape and build filter_o + // $matches[1] should be now something like "foo=bar" + + // detect multiple leaf components + // [TODO] Maybe this will make problems with filters containing brackets inside the value + if (stristr($matches[1], ')(')) { + return PEAR::raiseError("Filter parsing error: invalid filter syntax - multiple leaf components detected!"); + } else { + $filter_parts = preg_split('/(?|<|>=|<=)/', $matches[1], 2, PREG_SPLIT_DELIM_CAPTURE); + if (count($filter_parts) != 3) { + return PEAR::raiseError("Filter parsing error: invalid filter syntax - unknown matching rule used"); + } else { + $filter_o = new Net_LDAP2_Filter(); + // [TODO]: Do we need to escape at all? what about *-chars user provide and that should remain special? + // I think, those prevent escaping! We need to check against PERL Net::LDAP! + // $value_arr = Net_LDAP2_Util::escape_filter_value(array($filter_parts[2])); + // $value = $value_arr[0]; + $value = $filter_parts[2]; + $filter_o->_filter = '('.$filter_parts[0].$filter_parts[1].$value.')'; + return $filter_o; + } + } + } + } else { + // ERROR: Filter components must be enclosed in round brackets + return PEAR::raiseError("Filter parsing error: invalid filter syntax - filter components must be enclosed in round brackets"); + } + } + + /** + * Get the string representation of this filter + * + * This method runs through all filter objects and creates + * the string representation of the filter. If this + * filter object is a leaf filter, then it will return + * the string representation of this filter. + * + * @return string|Net_LDAP2_Error + */ + public function asString() + { + if ($this->isLeaf()) { + $return = $this->_filter; + } else { + $return = ''; + foreach ($this->_subfilters as $filter) { + $return = $return.$filter->asString(); + } + $return = '(' . $this->_match . $return . ')'; + } + return $return; + } + + /** + * Alias for perl interface as_string() + * + * @see asString() + * @return string|Net_LDAP2_Error + */ + public function as_string() + { + return $this->asString(); + } + + /** + * Print the text representation of the filter to FH, or the currently selected output handle if FH is not given + * + * This method is only for compatibility to the perl interface. + * However, the original method was called "print" but due to PHP language restrictions, + * we can't have a print() method. + * + * @param resource $FH (optional) A filehandle resource + * + * @return true|Net_LDAP2_Error + */ + public function printMe($FH = false) + { + if (!is_resource($FH)) { + if (PEAR::isError($FH)) { + return $FH; + } + $filter_str = $this->asString(); + if (PEAR::isError($filter_str)) { + return $filter_str; + } else { + print($filter_str); + } + } else { + $filter_str = $this->asString(); + if (PEAR::isError($filter_str)) { + return $filter_str; + } else { + $res = @fwrite($FH, $this->asString()); + if ($res == false) { + return PEAR::raiseError("Unable to write filter string to filehandle \$FH!"); + } + } + } + return true; + } + + /** + * This can be used to escape a string to provide a valid LDAP-Filter. + * + * LDAP will only recognise certain characters as the + * character istself if they are properly escaped. This is + * what this method does. + * The method can be called statically, so you can use it outside + * for your own purposes (eg for escaping only parts of strings) + * + * In fact, this is just a shorthand to {@link Net_LDAP2_Util::escape_filter_value()}. + * For upward compatibiliy reasons you are strongly encouraged to use the escape + * methods provided by the Net_LDAP2_Util class. + * + * @param string $value Any string who should be escaped + * + * @static + * @return string The string $string, but escaped + * @deprecated Do not use this method anymore, instead use Net_LDAP2_Util::escape_filter_value() directly + */ + public static function escape($value) + { + $return = Net_LDAP2_Util::escape_filter_value(array($value)); + return $return[0]; + } + + /** + * Is this a container or a leaf filter object? + * + * @access protected + * @return boolean + */ + protected function isLeaf() + { + if (count($this->_subfilters) > 0) { + return false; // Container! + } else { + return true; // Leaf! + } + } +} +?> diff --git a/thirdparty/pear/Net/LDAP2/LDIF.php b/thirdparty/pear/Net/LDAP2/LDIF.php new file mode 100644 index 0000000..34f3e75 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/LDIF.php @@ -0,0 +1,922 @@ + +* @copyright 2009 Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: LDIF.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; +require_once 'Net/LDAP2.php'; +require_once 'Net/LDAP2/Entry.php'; +require_once 'Net/LDAP2/Util.php'; + +/** +* LDIF capabilitys for Net_LDAP2, closely taken from PERLs Net::LDAP +* +* It provides a means to convert between Net_LDAP2_Entry objects and LDAP entries +* represented in LDIF format files. Reading and writing are supported and may +* manipulate single entries or lists of entries. +* +* Usage example: +* +* // Read and parse an ldif-file into Net_LDAP2_Entry objects +* // and print out the DNs. Store the entries for later use. +* require 'Net/LDAP2/LDIF.php'; +* $options = array( +* 'onerror' => 'die' +* ); +* $entries = array(); +* $ldif = new Net_LDAP2_LDIF('test.ldif', 'r', $options); +* do { +* $entry = $ldif->read_entry(); +* $dn = $entry->dn(); +* echo " done building entry: $dn\n"; +* array_push($entries, $entry); +* } while (!$ldif->eof()); +* $ldif->done(); +* +* +* // write those entries to another file +* $ldif = new Net_LDAP2_LDIF('test.out.ldif', 'w', $options); +* $ldif->write_entry($entries); +* $ldif->done(); +* +* +* @category Net +* @package Net_LDAP2 +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP22/ +* @see http://www.ietf.org/rfc/rfc2849.txt +* @todo Error handling should be PEARified +* @todo LDAPv3 controls are not implemented yet +*/ +class Net_LDAP2_LDIF extends PEAR +{ + /** + * Options + * + * @access protected + * @var array + */ + protected $_options = array('encode' => 'base64', + 'onerror' => null, + 'change' => 0, + 'lowercase' => 0, + 'sort' => 0, + 'version' => null, + 'wrap' => 78, + 'raw' => '' + ); + + /** + * Errorcache + * + * @access protected + * @var array + */ + protected $_error = array('error' => null, + 'line' => 0 + ); + + /** + * Filehandle for read/write + * + * @access protected + * @var array + */ + protected $_FH = null; + + /** + * Says, if we opened the filehandle ourselves + * + * @access protected + * @var array + */ + protected $_FH_opened = false; + + /** + * Linecounter for input file handle + * + * @access protected + * @var array + */ + protected $_input_line = 0; + + /** + * counter for processed entries + * + * @access protected + * @var int + */ + protected $_entrynum = 0; + + /** + * Mode we are working in + * + * Either 'r', 'a' or 'w' + * + * @access protected + * @var string + */ + protected $_mode = false; + + /** + * Tells, if the LDIF version string was already written + * + * @access protected + * @var boolean + */ + protected $_version_written = false; + + /** + * Cache for lines that have build the current entry + * + * @access protected + * @var boolean + */ + protected $_lines_cur = array(); + + /** + * Cache for lines that will build the next entry + * + * @access protected + * @var boolean + */ + protected $_lines_next = array(); + + /** + * Open LDIF file for reading or for writing + * + * new (FILE): + * Open the file read-only. FILE may be the name of a file + * or an already open filehandle. + * If the file doesn't exist, it will be created if in write mode. + * + * new (FILE, MODE, OPTIONS): + * Open the file with the given MODE (see PHPs fopen()), eg "w" or "a". + * FILE may be the name of a file or an already open filehandle. + * PERLs Net_LDAP2 "FILE|" mode does not work curently. + * + * OPTIONS is an associative array and may contain: + * encode => 'none' | 'canonical' | 'base64' + * Some DN values in LDIF cannot be written verbatim and have to be encoded in some way: + * 'none' No encoding. + * 'canonical' See "canonical_dn()" in Net::LDAP::Util. + * 'base64' Use base64. (default, this differs from the Perl interface. + * The perl default is "none"!) + * + * onerror => 'die' | 'warn' | NULL + * Specify what happens when an error is detected. + * 'die' Net_LDAP2_LDIF will croak with an appropriate message. + * 'warn' Net_LDAP2_LDIF will warn (echo) with an appropriate message. + * NULL Net_LDAP2_LDIF will not warn (default), use error(). + * + * change => 1 + * Write entry changes to the LDIF file instead of the entries itself. I.e. write LDAP + * operations acting on the entries to the file instead of the entries contents. + * This writes the changes usually carried out by an update() to the LDIF file. + * + * lowercase => 1 + * Convert attribute names to lowercase when writing. + * + * sort => 1 + * Sort attribute names when writing entries according to the rule: + * objectclass first then all other attributes alphabetically sorted by attribute name + * + * version => '1' + * Set the LDIF version to write to the resulting LDIF file. + * According to RFC 2849 currently the only legal value for this option is 1. + * When this option is set Net_LDAP2_LDIF tries to adhere more strictly to + * the LDIF specification in RFC2489 in a few places. + * The default is NULL meaning no version information is written to the LDIF file. + * + * wrap => 78 + * Number of columns where output line wrapping shall occur. + * Default is 78. Setting it to 40 or lower inhibits wrapping. + * + * raw => REGEX + * Use REGEX to denote the names of attributes that are to be + * considered binary in search results if writing entries. + * Example: raw => "/(?i:^jpegPhoto|;binary)/i" + * + * @param string|ressource $file Filename or filehandle + * @param string $mode Mode to open filename + * @param array $options Options like described above + */ + public function __construct($file, $mode = 'r', $options = array()) + { + $this->PEAR('Net_LDAP2_Error'); // default error class + + // First, parse options + // todo: maybe implement further checks on possible values + foreach ($options as $option => $value) { + if (!array_key_exists($option, $this->_options)) { + $this->dropError('Net_LDAP2_LDIF error: option '.$option.' not known!'); + return; + } else { + $this->_options[$option] = strtolower($value); + } + } + + // setup LDIF class + $this->version($this->_options['version']); + + // setup file mode + if (!preg_match('/^[rwa]\+?$/', $mode)) { + $this->dropError('Net_LDAP2_LDIF error: file mode '.$mode.' not supported!'); + } else { + $this->_mode = $mode; + + // setup filehandle + if (is_resource($file)) { + // TODO: checks on mode possible? + $this->_FH =& $file; + } else { + $imode = substr($this->_mode, 0, 1); + if ($imode == 'r') { + if (!file_exists($file)) { + $this->dropError('Unable to open '.$file.' for read: file not found'); + $this->_mode = false; + } + if (!is_readable($file)) { + $this->dropError('Unable to open '.$file.' for read: permission denied'); + $this->_mode = false; + } + } + + if (($imode == 'w' || $imode == 'a')) { + if (file_exists($file)) { + if (!is_writable($file)) { + $this->dropError('Unable to open '.$file.' for write: permission denied'); + $this->_mode = false; + } + } else { + if (!@touch($file)) { + $this->dropError('Unable to create '.$file.' for write: permission denied'); + $this->_mode = false; + } + } + } + + if ($this->_mode) { + $this->_FH = @fopen($file, $this->_mode); + if (false === $this->_FH) { + // Fallback; should never be reached if tests above are good enough! + $this->dropError('Net_LDAP2_LDIF error: Could not open file '.$file); + } else { + $this->_FH_opened = true; + } + } + } + } + } + + /** + * Read one entry from the file and return it as a Net::LDAP::Entry object. + * + * @return Net_LDAP2_Entry + */ + public function read_entry() + { + // read fresh lines, set them as current lines and create the entry + $attrs = $this->next_lines(true); + if (count($attrs) > 0) { + $this->_lines_cur = $attrs; + } + return $this->current_entry(); + } + + /** + * Returns true when the end of the file is reached. + * + * @return boolean + */ + public function eof() + { + return feof($this->_FH); + } + + /** + * Write the entry or entries to the LDIF file. + * + * If you want to build an LDIF file containing several entries AND + * you want to call write_entry() several times, you must open the filehandle + * in append mode ("a"), otherwise you will always get the last entry only. + * + * @param Net_LDAP2_Entry|array $entries Entry or array of entries + * + * @return void + * @todo implement operations on whole entries (adding a whole entry) + */ + public function write_entry($entries) + { + if (!is_array($entries)) { + $entries = array($entries); + } + + foreach ($entries as $entry) { + $this->_entrynum++; + if (!$entry instanceof Net_LDAP2_Entry) { + $this->dropError('Net_LDAP2_LDIF error: entry '.$this->_entrynum.' is not an Net_LDAP2_Entry object'); + } else { + if ($this->_options['change']) { + // LDIF change mode + // fetch change information from entry + $entry_attrs_changes = $entry->getChanges(); + $num_of_changes = count($entry_attrs_changes['add']) + + count($entry_attrs_changes['replace']) + + count($entry_attrs_changes['delete']); + + $is_changed = ($num_of_changes > 0 || $entry->willBeDeleted() || $entry->willBeMoved()); + + // write version if not done yet + // also write DN of entry + if ($is_changed) { + if (!$this->_version_written) { + $this->write_version(); + } + $this->writeDN($entry->currentDN()); + } + + // process changes + // TODO: consider DN add! + if ($entry->willBeDeleted()) { + $this->writeLine("changetype: delete".PHP_EOL); + } elseif ($entry->willBeMoved()) { + $this->writeLine("changetype: modrdn".PHP_EOL); + $olddn = Net_LDAP2_Util::ldap_explode_dn($entry->currentDN(), array('casefold' => 'none')); // maybe gives a bug if using multivalued RDNs + $oldrdn = array_shift($olddn); + $oldparent = implode(',', $olddn); + $newdn = Net_LDAP2_Util::ldap_explode_dn($entry->dn(), array('casefold' => 'none')); // maybe gives a bug if using multivalued RDNs + $rdn = array_shift($newdn); + $parent = implode(',', $newdn); + $this->writeLine("newrdn: ".$rdn.PHP_EOL); + $this->writeLine("deleteoldrdn: 1".PHP_EOL); + if ($parent !== $oldparent) { + $this->writeLine("newsuperior: ".$parent.PHP_EOL); + } + // TODO: What if the entry has attribute changes as well? + // I think we should check for that and make a dummy + // entry with the changes that is written to the LDIF file + } elseif ($num_of_changes > 0) { + // write attribute change data + $this->writeLine("changetype: modify".PHP_EOL); + foreach ($entry_attrs_changes as $changetype => $entry_attrs) { + foreach ($entry_attrs as $attr_name => $attr_values) { + $this->writeLine("$changetype: $attr_name".PHP_EOL); + if ($attr_values !== null) $this->writeAttribute($attr_name, $attr_values, $changetype); + $this->writeLine("-".PHP_EOL); + } + } + } + + // finish this entrys data if we had changes + if ($is_changed) { + $this->finishEntry(); + } + } else { + // LDIF-content mode + // fetch attributes for further processing + $entry_attrs = $entry->getValues(); + + // sort and put objectclass-attrs to first position + if ($this->_options['sort']) { + ksort($entry_attrs); + if (array_key_exists('objectclass', $entry_attrs)) { + $oc = $entry_attrs['objectclass']; + unset($entry_attrs['objectclass']); + $entry_attrs = array_merge(array('objectclass' => $oc), $entry_attrs); + } + } + + // write data + if (!$this->_version_written) { + $this->write_version(); + } + $this->writeDN($entry->dn()); + foreach ($entry_attrs as $attr_name => $attr_values) { + $this->writeAttribute($attr_name, $attr_values); + } + $this->finishEntry(); + } + } + } + } + + /** + * Write version to LDIF + * + * If the object's version is defined, this method allows to explicitely write the version before an entry is written. + * If not called explicitely, it gets called automatically when writing the first entry. + * + * @return void + */ + public function write_version() + { + $this->_version_written = true; + if (!is_null($this->version())) { + return $this->writeLine('version: '.$this->version().PHP_EOL, 'Net_LDAP2_LDIF error: unable to write version'); + } + } + + /** + * Get or set LDIF version + * + * If called without arguments it returns the version of the LDIF file or NULL if no version has been set. + * If called with an argument it sets the LDIF version to VERSION. + * According to RFC 2849 currently the only legal value for VERSION is 1. + * + * @param int $version (optional) LDIF version to set + * + * @return int + */ + public function version($version = null) + { + if ($version !== null) { + if ($version != 1) { + $this->dropError('Net_LDAP2_LDIF error: illegal LDIF version set'); + } else { + $this->_options['version'] = $version; + } + } + return $this->_options['version']; + } + + /** + * Returns the file handle the Net_LDAP2_LDIF object reads from or writes to. + * + * You can, for example, use this to fetch the content of the LDIF file yourself + * + * @return null|resource + */ + public function &handle() + { + if (!is_resource($this->_FH)) { + $this->dropError('Net_LDAP2_LDIF error: invalid file resource'); + $null = null; + return $null; + } else { + return $this->_FH; + } + } + + /** + * Clean up + * + * This method signals that the LDIF object is no longer needed. + * You can use this to free up some memory and close the file handle. + * The file handle is only closed, if it was opened from Net_LDAP2_LDIF. + * + * @return void + */ + public function done() + { + // close FH if we opened it + if ($this->_FH_opened) { + fclose($this->handle()); + } + + // free variables + foreach (get_object_vars($this) as $name => $value) { + unset($this->$name); + } + } + + /** + * Returns last error message if error was found. + * + * Example: + * + * $ldif->someAction(); + * if ($ldif->error()) { + * echo "Error: ".$ldif->error()." at input line: ".$ldif->error_lines(); + * } + * + * + * @param boolean $as_string If set to true, only the message is returned + * + * @return false|Net_LDAP2_Error + */ + public function error($as_string = false) + { + if (Net_LDAP2::isError($this->_error['error'])) { + return ($as_string)? $this->_error['error']->getMessage() : $this->_error['error']; + } else { + return false; + } + } + + /** + * Returns lines that resulted in error. + * + * Perl returns an array of faulty lines in list context, + * but we always just return an int because of PHPs language. + * + * @return int + */ + public function error_lines() + { + return $this->_error['line']; + } + + /** + * Returns the current Net::LDAP::Entry object. + * + * @return Net_LDAP2_Entry|false + */ + public function current_entry() + { + return $this->parseLines($this->current_lines()); + } + + /** + * Parse LDIF lines of one entry into an Net_LDAP2_Entry object + * + * @param array $lines LDIF lines for one entry + * + * @return Net_LDAP2_Entry|false Net_LDAP2_Entry object for those lines + * @todo what about file inclusions and urls? "jpegphoto:< file:///usr/local/directory/photos/fiona.jpg" + */ + public function parseLines($lines) + { + // parse lines into an array of attributes and build the entry + $attributes = array(); + $dn = false; + foreach ($lines as $line) { + if (preg_match('/^(\w+)(:|::|:<)\s(.+)$/', $line, $matches)) { + $attr =& $matches[1]; + $delim =& $matches[2]; + $data =& $matches[3]; + + if ($delim == ':') { + // normal data + $attributes[$attr][] = $data; + } elseif ($delim == '::') { + // base64 data + $attributes[$attr][] = base64_decode($data); + } elseif ($delim == ':<') { + // file inclusion + // TODO: Is this the job of the LDAP-client or the server? + $this->dropError('File inclusions are currently not supported'); + //$attributes[$attr][] = ...; + } else { + // since the pattern above, the delimeter cannot be something else. + $this->dropError('Net_LDAP2_LDIF parsing error: invalid syntax at parsing entry line: '.$line); + continue; + } + + if (strtolower($attr) == 'dn') { + // DN line detected + $dn = $attributes[$attr][0]; // save possibly decoded DN + unset($attributes[$attr]); // remove wrongly added "dn: " attribute + } + } else { + // line not in "attr: value" format -> ignore + // maybe we should rise an error here, but this should be covered by + // next_lines() already. A problem arises, if users try to feed data of + // several entries to this method - the resulting entry will + // get wrong attributes. However, this is already mentioned in the + // methods documentation above. + } + } + + if (false === $dn) { + $this->dropError('Net_LDAP2_LDIF parsing error: unable to detect DN for entry'); + return false; + } else { + $newentry = Net_LDAP2_Entry::createFresh($dn, $attributes); + return $newentry; + } + } + + /** + * Returns the lines that generated the current Net::LDAP::Entry object. + * + * Note that this returns an empty array if no lines have been read so far. + * + * @return array Array of lines + */ + public function current_lines() + { + return $this->_lines_cur; + } + + /** + * Returns the lines that will generate the next Net::LDAP::Entry object. + * + * If you set $force to TRUE then you can iterate over the lines that build + * up entries manually. Otherwise, iterating is done using {@link read_entry()}. + * Force will move the file pointer forward, thus returning the next entries lines. + * + * Wrapped lines will be unwrapped. Comments are stripped. + * + * @param boolean $force Set this to true if you want to iterate over the lines manually + * + * @return array + */ + public function next_lines($force = false) + { + // if we already have those lines, just return them, otherwise read + if (count($this->_lines_next) == 0 || $force) { + $this->_lines_next = array(); // empty in case something was left (if used $force) + $entry_done = false; + $fh = &$this->handle(); + $commentmode = false; // if we are in an comment, for wrapping purposes + $datalines_read = 0; // how many lines with data we have read + + while (!$entry_done && !$this->eof()) { + $this->_input_line++; + // Read line. Remove line endings, we want only data; + // this is okay since ending spaces should be encoded + $data = rtrim(fgets($fh)); + if ($data === false) { + // error only, if EOF not reached after fgets() call + if (!$this->eof()) { + $this->dropError('Net_LDAP2_LDIF error: error reading from file at input line '.$this->_input_line, $this->_input_line); + } + break; + } else { + if (count($this->_lines_next) > 0 && preg_match('/^$/', $data)) { + // Entry is finished if we have an empty line after we had data + $entry_done = true; + + // Look ahead if the next EOF is nearby. Comments and empty + // lines at the file end may cause problems otherwise + $current_pos = ftell($fh); + $data = fgets($fh); + while (!feof($fh)) { + if (preg_match('/^\s*$/', $data) || preg_match('/^#/', $data)) { + // only empty lines or comments, continue to seek + // TODO: Known bug: Wrappings for comments are okay but are treaten as + // error, since we do not honor comment mode here. + // This should be a very theoretically case, however + // i am willing to fix this if really necessary. + $this->_input_line++; + $current_pos = ftell($fh); + $data = fgets($fh); + } else { + // Data found if non emtpy line and not a comment!! + // Rewind to position prior last read and stop lookahead + fseek($fh, $current_pos); + break; + } + } + // now we have either the file pointer at the beginning of + // a new data position or at the end of file causing feof() to return true + + } else { + // build lines + if (preg_match('/^version:\s(.+)$/', $data, $match)) { + // version statement, set version + $this->version($match[1]); + } elseif (preg_match('/^\w+::?\s.+$/', $data)) { + // normal attribute: add line + $commentmode = false; + $this->_lines_next[] = trim($data); + $datalines_read++; + } elseif (preg_match('/^\s(.+)$/', $data, $matches)) { + // wrapped data: unwrap if not in comment mode + if (!$commentmode) { + if ($datalines_read == 0) { + // first line of entry: wrapped data is illegal + $this->dropError('Net_LDAP2_LDIF error: illegal wrapping at input line '.$this->_input_line, $this->_input_line); + } else { + $last = array_pop($this->_lines_next); + $last = $last.trim($matches[1]); + $this->_lines_next[] = $last; + $datalines_read++; + } + } + } elseif (preg_match('/^#/', $data)) { + // LDIF comments + $commentmode = true; + } elseif (preg_match('/^\s*$/', $data)) { + // empty line but we had no data for this + // entry, so just ignore this line + $commentmode = false; + } else { + $this->dropError('Net_LDAP2_LDIF error: invalid syntax at input line '.$this->_input_line, $this->_input_line); + continue; + } + + } + } + } + } + return $this->_lines_next; + } + + /** + * Convert an attribute and value to LDIF string representation + * + * It honors correct encoding of values according to RFC 2849. + * Line wrapping will occur at the configured maximum but only if + * the value is greater than 40 chars. + * + * @param string $attr_name Name of the attribute + * @param string $attr_value Value of the attribute + * + * @access protected + * @return string LDIF string for that attribute and value + */ + protected function convertAttribute($attr_name, $attr_value) + { + // Handle empty attribute or process + if (strlen($attr_value) == 0) { + $attr_value = " "; + } else { + $base64 = false; + // ASCII-chars that are NOT safe for the + // start and for being inside the value. + // These are the int values of those chars. + $unsafe_init = array(0, 10, 13, 32, 58, 60); + $unsafe = array(0, 10, 13); + + // Test for illegal init char + $init_ord = ord(substr($attr_value, 0, 1)); + if ($init_ord > 127 || in_array($init_ord, $unsafe_init)) { + $base64 = true; + } + + // Test for illegal content char + for ($i = 0; $i < strlen($attr_value); $i++) { + $char_ord = ord(substr($attr_value, $i, 1)); + if ($char_ord > 127 || in_array($char_ord, $unsafe)) { + $base64 = true; + } + } + + // Test for ending space + if (substr($attr_value, -1) == ' ') { + $base64 = true; + } + + // If converting is needed, do it + // Either we have some special chars or a matching "raw" regex + if ($base64 || ($this->_options['raw'] && preg_match($this->_options['raw'], $attr_name))) { + $attr_name .= ':'; + $attr_value = base64_encode($attr_value); + } + + // Lowercase attr names if requested + if ($this->_options['lowercase']) $attr_name = strtolower($attr_name); + + // Handle line wrapping + if ($this->_options['wrap'] > 40 && strlen($attr_value) > $this->_options['wrap']) { + $attr_value = wordwrap($attr_value, $this->_options['wrap'], PHP_EOL." ", true); + } + } + + return $attr_name.': '.$attr_value; + } + + /** + * Convert an entries DN to LDIF string representation + * + * It honors correct encoding of values according to RFC 2849. + * + * @param string $dn UTF8-Encoded DN + * + * @access protected + * @return string LDIF string for that DN + * @todo I am not sure, if the UTF8 stuff is correctly handled right now + */ + protected function convertDN($dn) + { + $base64 = false; + // ASCII-chars that are NOT safe for the + // start and for being inside the dn. + // These are the int values of those chars. + $unsafe_init = array(0, 10, 13, 32, 58, 60); + $unsafe = array(0, 10, 13); + + // Test for illegal init char + $init_ord = ord(substr($dn, 0, 1)); + if ($init_ord >= 127 || in_array($init_ord, $unsafe_init)) { + $base64 = true; + } + + // Test for illegal content char + for ($i = 0; $i < strlen($dn); $i++) { + $char = substr($dn, $i, 1); + if (ord($char) >= 127 || in_array($init_ord, $unsafe)) { + $base64 = true; + } + } + + // Test for ending space + if (substr($dn, -1) == ' ') { + $base64 = true; + } + + // if converting is needed, do it + return ($base64)? 'dn:: '.base64_encode($dn) : 'dn: '.$dn; + } + + /** + * Writes an attribute to the filehandle + * + * @param string $attr_name Name of the attribute + * @param string|array $attr_values Single attribute value or array with attribute values + * + * @access protected + * @return void + */ + protected function writeAttribute($attr_name, $attr_values) + { + // write out attribute content + if (!is_array($attr_values)) { + $attr_values = array($attr_values); + } + foreach ($attr_values as $attr_val) { + $line = $this->convertAttribute($attr_name, $attr_val).PHP_EOL; + $this->writeLine($line, 'Net_LDAP2_LDIF error: unable to write attribute '.$attr_name.' of entry '.$this->_entrynum); + } + } + + /** + * Writes a DN to the filehandle + * + * @param string $dn DN to write + * + * @access protected + * @return void + */ + protected function writeDN($dn) + { + // prepare DN + if ($this->_options['encode'] == 'base64') { + $dn = $this->convertDN($dn).PHP_EOL; + } elseif ($this->_options['encode'] == 'canonical') { + $dn = Net_LDAP2_Util::canonical_dn($dn, array('casefold' => 'none')).PHP_EOL; + } else { + $dn = $dn.PHP_EOL; + } + $this->writeLine($dn, 'Net_LDAP2_LDIF error: unable to write DN of entry '.$this->_entrynum); + } + + /** + * Finishes an LDIF entry + * + * @access protected + * @return void + */ + protected function finishEntry() + { + $this->writeLine(PHP_EOL, 'Net_LDAP2_LDIF error: unable to close entry '.$this->_entrynum); + } + + /** + * Just write an arbitary line to the filehandle + * + * @param string $line Content to write + * @param string $error If error occurs, drop this message + * + * @access protected + * @return true|false + */ + protected function writeLine($line, $error = 'Net_LDAP2_LDIF error: unable to write to filehandle') + { + if (is_resource($this->handle()) && fwrite($this->handle(), $line, strlen($line)) === false) { + $this->dropError($error); + return false; + } else { + return true; + } + } + + /** + * Optionally raises an error and pushes the error on the error cache + * + * @param string $msg Errortext + * @param int $line Line in the LDIF that caused the error + * + * @access protected + * @return void + */ + protected function dropError($msg, $line = null) + { + $this->_error['error'] = new Net_LDAP2_Error($msg); + if ($line !== null) $this->_error['line'] = $line; + + if ($this->_options['onerror'] == 'die') { + die($msg.PHP_EOL); + } elseif ($this->_options['onerror'] == 'warn') { + echo $msg.PHP_EOL; + } + } +} +?> diff --git a/thirdparty/pear/Net/LDAP2/RootDSE.php b/thirdparty/pear/Net/LDAP2/RootDSE.php new file mode 100644 index 0000000..8dc81fd --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/RootDSE.php @@ -0,0 +1,240 @@ + +* @copyright 2009 Jan Wagner +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: RootDSE.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; + +/** +* Getting the rootDSE entry of a LDAP server +* +* @category Net +* @package Net_LDAP2 +* @author Jan Wagner +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP22/ +*/ +class Net_LDAP2_RootDSE extends PEAR +{ + /** + * @access protected + * @var object Net_LDAP2_Entry + **/ + protected $_entry; + + /** + * Class constructor + * + * @param Net_LDAP2_Entry &$entry Net_LDAP2_Entry object of the RootDSE + */ + protected function __construct(&$entry) + { + $this->_entry = $entry; + } + + /** + * Fetches a RootDSE object from an LDAP connection + * + * @param Net_LDAP2 $ldap Directory from which the RootDSE should be fetched + * @param array $attrs Array of attributes to search for + * + * @access static + * @return Net_LDAP2_RootDSE|Net_LDAP2_Error + */ + public static function fetch($ldap, $attrs = null) + { + if (!$ldap instanceof Net_LDAP2) { + return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!"); + } + + if (is_array($attrs) && count($attrs) > 0 ) { + $attributes = $attrs; + } else { + $attributes = array('vendorName', + 'vendorVersion', + 'namingContexts', + 'altServer', + 'supportedExtension', + 'supportedControl', + 'supportedSASLMechanisms', + 'supportedLDAPVersion', + 'subschemaSubentry' ); + } + $result = $ldap->search('', '(objectClass=*)', array('attributes' => $attributes, 'scope' => 'base')); + if (self::isError($result)) { + return $result; + } + $entry = $result->shiftEntry(); + if (false === $entry) { + return PEAR::raiseError('Could not fetch RootDSE entry'); + } + $ret = new Net_LDAP2_RootDSE($entry); + return $ret; + } + + /** + * Gets the requested attribute value + * + * Same usuage as {@link Net_LDAP2_Entry::getValue()} + * + * @param string $attr Attribute name + * @param array $options Array of options + * + * @access public + * @return mixed Net_LDAP2_Error object or attribute values + * @see Net_LDAP2_Entry::get_value() + */ + public function getValue($attr = '', $options = '') + { + return $this->_entry->get_value($attr, $options); + } + + /** + * Alias function of getValue() for perl-ldap interface + * + * @see getValue() + * @return mixed + */ + public function get_value() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'getValue' ), $args); + } + + /** + * Determines if the extension is supported + * + * @param array $oids Array of oids to check + * + * @access public + * @return boolean + */ + public function supportedExtension($oids) + { + return $this->checkAttr($oids, 'supportedExtension'); + } + + /** + * Alias function of supportedExtension() for perl-ldap interface + * + * @see supportedExtension() + * @return boolean + */ + public function supported_extension() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'supportedExtension'), $args); + } + + /** + * Determines if the version is supported + * + * @param array $versions Versions to check + * + * @access public + * @return boolean + */ + public function supportedVersion($versions) + { + return $this->checkAttr($versions, 'supportedLDAPVersion'); + } + + /** + * Alias function of supportedVersion() for perl-ldap interface + * + * @see supportedVersion() + * @return boolean + */ + public function supported_version() + { + $args = func_get_args(); + return call_user_func_array(array(&$this, 'supportedVersion'), $args); + } + + /** + * Determines if the control is supported + * + * @param array $oids Control oids to check + * + * @access public + * @return boolean + */ + public function supportedControl($oids) + { + return $this->checkAttr($oids, 'supportedControl'); + } + + /** + * Alias function of supportedControl() for perl-ldap interface + * + * @see supportedControl() + * @return boolean + */ + public function supported_control() + { + $args = func_get_args(); + return call_user_func_array(array(&$this, 'supportedControl' ), $args); + } + + /** + * Determines if the sasl mechanism is supported + * + * @param array $mechlist SASL mechanisms to check + * + * @access public + * @return boolean + */ + public function supportedSASLMechanism($mechlist) + { + return $this->checkAttr($mechlist, 'supportedSASLMechanisms'); + } + + /** + * Alias function of supportedSASLMechanism() for perl-ldap interface + * + * @see supportedSASLMechanism() + * @return boolean + */ + public function supported_sasl_mechanism() + { + $args = func_get_args(); + return call_user_func_array(array(&$this, 'supportedSASLMechanism'), $args); + } + + /** + * Checks for existance of value in attribute + * + * @param array $values values to check + * @param string $attr attribute name + * + * @access protected + * @return boolean + */ + protected function checkAttr($values, $attr) + { + if (!is_array($values)) $values = array($values); + + foreach ($values as $value) { + if (!@in_array($value, $this->get_value($attr, 'all'))) { + return false; + } + } + return true; + } +} + +?> diff --git a/thirdparty/pear/Net/LDAP2/Schema.php b/thirdparty/pear/Net/LDAP2/Schema.php new file mode 100644 index 0000000..b590eab --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/Schema.php @@ -0,0 +1,516 @@ + +* @author Benedikt Hallinger +* @copyright 2009 Jan Wagner, Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: Schema.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +* @todo see the comment at the end of the file +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; + +/** +* Syntax definitions +* +* Please don't forget to add binary attributes to isBinary() below +* to support proper value fetching from Net_LDAP2_Entry +*/ +define('NET_LDAP2_SYNTAX_BOOLEAN', '1.3.6.1.4.1.1466.115.121.1.7'); +define('NET_LDAP2_SYNTAX_DIRECTORY_STRING', '1.3.6.1.4.1.1466.115.121.1.15'); +define('NET_LDAP2_SYNTAX_DISTINGUISHED_NAME', '1.3.6.1.4.1.1466.115.121.1.12'); +define('NET_LDAP2_SYNTAX_INTEGER', '1.3.6.1.4.1.1466.115.121.1.27'); +define('NET_LDAP2_SYNTAX_JPEG', '1.3.6.1.4.1.1466.115.121.1.28'); +define('NET_LDAP2_SYNTAX_NUMERIC_STRING', '1.3.6.1.4.1.1466.115.121.1.36'); +define('NET_LDAP2_SYNTAX_OID', '1.3.6.1.4.1.1466.115.121.1.38'); +define('NET_LDAP2_SYNTAX_OCTET_STRING', '1.3.6.1.4.1.1466.115.121.1.40'); + +/** +* Load an LDAP Schema and provide information +* +* This class takes a Subschema entry, parses this information +* and makes it available in an array. Most of the code has been +* inspired by perl-ldap( http://perl-ldap.sourceforge.net). +* You will find portions of their implementation in here. +* +* @category Net +* @package Net_LDAP2 +* @author Jan Wagner +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP22/ +*/ +class Net_LDAP2_Schema extends PEAR +{ + /** + * Map of entry types to ldap attributes of subschema entry + * + * @access public + * @var array + */ + public $types = array( + 'attribute' => 'attributeTypes', + 'ditcontentrule' => 'dITContentRules', + 'ditstructurerule' => 'dITStructureRules', + 'matchingrule' => 'matchingRules', + 'matchingruleuse' => 'matchingRuleUse', + 'nameform' => 'nameForms', + 'objectclass' => 'objectClasses', + 'syntax' => 'ldapSyntaxes' + ); + + /** + * Array of entries belonging to this type + * + * @access protected + * @var array + */ + protected $_attributeTypes = array(); + protected $_matchingRules = array(); + protected $_matchingRuleUse = array(); + protected $_ldapSyntaxes = array(); + protected $_objectClasses = array(); + protected $_dITContentRules = array(); + protected $_dITStructureRules = array(); + protected $_nameForms = array(); + + + /** + * hash of all fetched oids + * + * @access protected + * @var array + */ + protected $_oids = array(); + + /** + * Tells if the schema is initialized + * + * @access protected + * @var boolean + * @see parse(), get() + */ + protected $_initialized = false; + + + /** + * Constructor of the class + * + * @access protected + */ + protected function __construct() + { + $this->PEAR('Net_LDAP2_Error'); // default error class + } + + /** + * Fetch the Schema from an LDAP connection + * + * @param Net_LDAP2 $ldap LDAP connection + * @param string $dn (optional) Subschema entry dn + * + * @access public + * @return Net_LDAP2_Schema|NET_LDAP2_Error + */ + public function fetch($ldap, $dn = null) + { + if (!$ldap instanceof Net_LDAP2) { + return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!"); + } + + $schema_o = new Net_LDAP2_Schema(); + + if (is_null($dn)) { + // get the subschema entry via root dse + $dse = $ldap->rootDSE(array('subschemaSubentry')); + if (false == Net_LDAP2::isError($dse)) { + $base = $dse->getValue('subschemaSubentry', 'single'); + if (!Net_LDAP2::isError($base)) { + $dn = $base; + } + } + } + + // Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that incorrectly + // call this entry subSchemaSubentry instead of subschemaSubentry. + // Note the correct case/spelling as per RFC 2251. + if (is_null($dn)) { + // get the subschema entry via root dse + $dse = $ldap->rootDSE(array('subSchemaSubentry')); + if (false == Net_LDAP2::isError($dse)) { + $base = $dse->getValue('subSchemaSubentry', 'single'); + if (!Net_LDAP2::isError($base)) { + $dn = $base; + } + } + } + + // Final fallback case where there is no subschemaSubentry attribute + // in the root DSE (this is a bug for an LDAP v3 server so report this + // to your LDAP vendor if you get this far). + if (is_null($dn)) { + $dn = 'cn=Subschema'; + } + + // fetch the subschema entry + $result = $ldap->search($dn, '(objectClass=*)', + array('attributes' => array_values($schema_o->types), + 'scope' => 'base')); + if (Net_LDAP2::isError($result)) { + return $result; + } + + $entry = $result->shiftEntry(); + if (!$entry instanceof Net_LDAP2_Entry) { + return PEAR::raiseError('Could not fetch Subschema entry'); + } + + $schema_o->parse($entry); + return $schema_o; + } + + /** + * Return a hash of entries for the given type + * + * Returns a hash of entry for th givene type. Types may be: + * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules, + * matchingruleuses, nameforms, syntaxes + * + * @param string $type Type to fetch + * + * @access public + * @return array|Net_LDAP2_Error Array or Net_LDAP2_Error + */ + public function &getAll($type) + { + $map = array('objectclasses' => &$this->_objectClasses, + 'attributes' => &$this->_attributeTypes, + 'ditcontentrules' => &$this->_dITContentRules, + 'ditstructurerules' => &$this->_dITStructureRules, + 'matchingrules' => &$this->_matchingRules, + 'matchingruleuses' => &$this->_matchingRuleUse, + 'nameforms' => &$this->_nameForms, + 'syntaxes' => &$this->_ldapSyntaxes ); + + $key = strtolower($type); + $ret = ((key_exists($key, $map)) ? $map[$key] : PEAR::raiseError("Unknown type $type")); + return $ret; + } + + /** + * Return a specific entry + * + * @param string $type Type of name + * @param string $name Name or OID to fetch + * + * @access public + * @return mixed Entry or Net_LDAP2_Error + */ + public function &get($type, $name) + { + if ($this->_initialized) { + $type = strtolower($type); + if (false == key_exists($type, $this->types)) { + return PEAR::raiseError("No such type $type"); + } + + $name = strtolower($name); + $type_var = &$this->{'_' . $this->types[$type]}; + + if (key_exists($name, $type_var)) { + return $type_var[$name]; + } elseif (key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) { + return $this->_oids[$name]; + } else { + return PEAR::raiseError("Could not find $type $name"); + } + } else { + $return = null; + return $return; + } + } + + + /** + * Fetches attributes that MAY be present in the given objectclass + * + * @param string $oc Name or OID of objectclass + * + * @access public + * @return array|Net_LDAP2_Error Array with attributes or Net_LDAP2_Error + */ + public function may($oc) + { + return $this->_getAttr($oc, 'may'); + } + + /** + * Fetches attributes that MUST be present in the given objectclass + * + * @param string $oc Name or OID of objectclass + * + * @access public + * @return array|Net_LDAP2_Error Array with attributes or Net_LDAP2_Error + */ + public function must($oc) + { + return $this->_getAttr($oc, 'must'); + } + + /** + * Fetches the given attribute from the given objectclass + * + * @param string $oc Name or OID of objectclass + * @param string $attr Name of attribute to fetch + * + * @access protected + * @return array|Net_LDAP2_Error The attribute or Net_LDAP2_Error + */ + protected function _getAttr($oc, $attr) + { + $oc = strtolower($oc); + if (key_exists($oc, $this->_objectClasses) && key_exists($attr, $this->_objectClasses[$oc])) { + return $this->_objectClasses[$oc][$attr]; + } elseif (key_exists($oc, $this->_oids) && + $this->_oids[$oc]['type'] == 'objectclass' && + key_exists($attr, $this->_oids[$oc])) { + return $this->_oids[$oc][$attr]; + } else { + return PEAR::raiseError("Could not find $attr attributes for $oc "); + } + } + + /** + * Returns the name(s) of the immediate superclass(es) + * + * @param string $oc Name or OID of objectclass + * + * @access public + * @return array|Net_LDAP2_Error Array of names or Net_LDAP2_Error + */ + public function superclass($oc) + { + $o = $this->get('objectclass', $oc); + if (Net_LDAP2::isError($o)) { + return $o; + } + return (key_exists('sup', $o) ? $o['sup'] : array()); + } + + /** + * Parses the schema of the given Subschema entry + * + * @param Net_LDAP2_Entry &$entry Subschema entry + * + * @access public + * @return void + */ + public function parse(&$entry) + { + foreach ($this->types as $type => $attr) { + // initialize map type to entry + $type_var = '_' . $attr; + $this->{$type_var} = array(); + + // get values for this type + if ($entry->exists($attr)) { + $values = $entry->getValue($attr); + if (is_array($values)) { + foreach ($values as $value) { + + unset($schema_entry); // this was a real mess without it + + // get the schema entry + $schema_entry = $this->_parse_entry($value); + + // set the type + $schema_entry['type'] = $type; + + // save a ref in $_oids + $this->_oids[$schema_entry['oid']] = &$schema_entry; + + // save refs for all names in type map + $names = $schema_entry['aliases']; + array_push($names, $schema_entry['name']); + foreach ($names as $name) { + $this->{$type_var}[strtolower($name)] = &$schema_entry; + } + } + } + } + } + $this->_initialized = true; + } + + /** + * Parses an attribute value into a schema entry + * + * @param string $value Attribute value + * + * @access protected + * @return array|false Schema entry array or false + */ + protected function &_parse_entry($value) + { + // tokens that have no value associated + $noValue = array('single-value', + 'obsolete', + 'collective', + 'no-user-modification', + 'abstract', + 'structural', + 'auxiliary'); + + // tokens that can have multiple values + $multiValue = array('must', 'may', 'sup'); + + $schema_entry = array('aliases' => array()); // initilization + + $tokens = $this->_tokenize($value); // get an array of tokens + + // remove surrounding brackets + if ($tokens[0] == '(') array_shift($tokens); + if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-( + + $schema_entry['oid'] = array_shift($tokens); // first token is the oid + + // cycle over the tokens until none are left + while (count($tokens) > 0) { + $token = strtolower(array_shift($tokens)); + if (in_array($token, $noValue)) { + $schema_entry[$token] = 1; // single value token + } else { + // this one follows a string or a list if it is multivalued + if (($schema_entry[$token] = array_shift($tokens)) == '(') { + // this creates the list of values and cycles through the tokens + // until the end of the list is reached ')' + $schema_entry[$token] = array(); + while ($tmp = array_shift($tokens)) { + if ($tmp == ')') break; + if ($tmp != '$') array_push($schema_entry[$token], $tmp); + } + } + // create a array if the value should be multivalued but was not + if (in_array($token, $multiValue) && !is_array($schema_entry[$token])) { + $schema_entry[$token] = array($schema_entry[$token]); + } + } + } + // get max length from syntax + if (key_exists('syntax', $schema_entry)) { + if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) { + $schema_entry['max_length'] = $matches[1]; + } + } + // force a name + if (empty($schema_entry['name'])) { + $schema_entry['name'] = $schema_entry['oid']; + } + // make one name the default and put the other ones into aliases + if (is_array($schema_entry['name'])) { + $aliases = $schema_entry['name']; + $schema_entry['name'] = array_shift($aliases); + $schema_entry['aliases'] = $aliases; + } + return $schema_entry; + } + + /** + * Tokenizes the given value into an array of tokens + * + * @param string $value String to parse + * + * @access protected + * @return array Array of tokens + */ + protected function _tokenize($value) + { + $tokens = array(); // array of tokens + $matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns + + // this one is taken from perl-ldap, modified for php + $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x"; + + /** + * This one matches one big pattern wherin only one of the three subpatterns matched + * We are interested in the subpatterns that matched. If it matched its value will be + * non-empty and so it is a token. Tokens may be round brackets, a string, or a string + * enclosed by ' + */ + preg_match_all($pattern, $value, $matches); + + for ($i = 0; $i < count($matches[0]); $i++) { // number of tokens (full pattern match) + for ($j = 1; $j < 4; $j++) { // each subpattern + if (null != trim($matches[$j][$i])) { // pattern match in this subpattern + $tokens[$i] = trim($matches[$j][$i]); // this is the token + } + } + } + return $tokens; + } + + /** + * Returns wether a attribute syntax is binary or not + * + * This method gets used by Net_LDAP2_Entry to decide which + * PHP function needs to be used to fetch the value in the + * proper format (e.g. binary or string) + * + * @param string $attribute The name of the attribute (eg.: 'sn') + * + * @access public + * @return boolean + */ + public function isBinary($attribute) + { + $return = false; // default to false + + // This list contains all syntax that should be treaten as + // containing binary values + // The Syntax Definitons go into constants at the top of this page + $syntax_binary = array( + NET_LDAP2_SYNTAX_OCTET_STRING, + NET_LDAP2_SYNTAX_JPEG + ); + + // Check Syntax + $attr_s = $this->get('attribute', $attribute); + if (Net_LDAP2::isError($attr_s)) { + // Attribute not found in schema + $return = false; // consider attr not binary + } elseif (isset($attr_s['syntax']) && in_array($attr_s['syntax'], $syntax_binary)) { + // Syntax is defined as binary in schema + $return = true; + } else { + // Syntax not defined as binary, or not found + // if attribute is a subtype, check superior attribute syntaxes + if (isset($attr_s['sup'])) { + foreach ($attr_s['sup'] as $superattr) { + $return = $this->isBinary($superattr); + if ($return) { + break; // stop checking parents since we are binary + } + } + } + } + + return $return; + } + + // [TODO] add method that allows us to see to which objectclasses a certain attribute belongs to + // it should return the result structured, e.g. sorted in "may" and "must". Optionally it should + // be able to return it just "flat", e.g. array_merge()d. + // We could use get_all() to achieve this easily, i think +} +?> diff --git a/thirdparty/pear/Net/LDAP2/SchemaCache.interface.php b/thirdparty/pear/Net/LDAP2/SchemaCache.interface.php new file mode 100644 index 0000000..e0c3094 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/SchemaCache.interface.php @@ -0,0 +1,59 @@ + +* @copyright 2009 Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: SchemaCache.interface.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Interface describing a custom schema cache object +* +* To implement a custom schema cache, one must implement this interface and +* pass the instanciated object to Net_LDAP2s registerSchemaCache() method. +*/ +interface Net_LDAP2_SchemaCache +{ + /** + * Return the schema object from the cache + * + * Net_LDAP2 will consider anything returned invalid, except + * a valid Net_LDAP2_Schema object. + * In case you return a Net_LDAP2_Error, this error will be routed + * to the return of the $ldap->schema() call. + * If you return something else, Net_LDAP2 will + * fetch a fresh Schema object from the LDAP server. + * + * You may want to implement a cache aging mechanism here too. + * + * @return Net_LDAP2_Schema|Net_LDAP2_Error|false + */ + public function loadSchema(); + + /** + * Store a schema object in the cache + * + * This method will be called, if Net_LDAP2 has fetched a fresh + * schema object from LDAP and wants to init or refresh the cache. + * + * In case of errors you may return a Net_LDAP2_Error which will + * be routet to the client. + * Note that doing this prevents, that the schema object fetched from LDAP + * will be given back to the client, so only return errors if storing + * of the cache is something crucial (e.g. for doing something else with it). + * Normaly you dont want to give back errors in which case Net_LDAP2 needs to + * fetch the schema once per script run and instead use the error + * returned from loadSchema(). + * + * @return true|Net_LDAP2_Error + */ + public function storeSchema($schema); +} diff --git a/thirdparty/pear/Net/LDAP2/Search.php b/thirdparty/pear/Net/LDAP2/Search.php new file mode 100644 index 0000000..de4fde1 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/Search.php @@ -0,0 +1,614 @@ + +* @author Benedikt Hallinger +* @copyright 2009 Tarjej Huse, Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: Search.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; + +/** +* Result set of an LDAP search +* +* @category Net +* @package Net_LDAP2 +* @author Tarjej Huse +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP22/ +*/ +class Net_LDAP2_Search extends PEAR implements Iterator +{ + /** + * Search result identifier + * + * @access protected + * @var resource + */ + protected $_search; + + /** + * LDAP resource link + * + * @access protected + * @var resource + */ + protected $_link; + + /** + * Net_LDAP2 object + * + * A reference of the Net_LDAP2 object for passing to Net_LDAP2_Entry + * + * @access protected + * @var object Net_LDAP2 + */ + protected $_ldap; + + /** + * Result entry identifier + * + * @access protected + * @var resource + */ + protected $_entry = null; + + /** + * The errorcode the search got + * + * Some errorcodes might be of interest, but might not be best handled as errors. + * examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indicates a huge search. + * Incomplete results are returned. If you just want to check if there's anything in the search. + * than this is a point to handle. + * 32 - no such object - search here returns a count of 0. + * + * @access protected + * @var int + */ + protected $_errorCode = 0; // if not set - sucess! + + /** + * Cache for all entries already fetched from iterator interface + * + * @access protected + * @var array + */ + protected $_iteratorCache = array(); + + /** + * What attributes we searched for + * + * The $attributes array contains the names of the searched attributes and gets + * passed from $Net_LDAP2->search() so the Net_LDAP2_Search object can tell + * what attributes was searched for ({@link searchedAttrs()) + * + * This variable gets set from the constructor and returned + * from {@link searchedAttrs()} + * + * @access protected + * @var array + */ + protected $_searchedAttrs = array(); + + /** + * Cache variable for storing entries fetched internally + * + * This currently is only used by {@link pop_entry()} + * + * @access protected + * @var array + */ + protected $_entry_cache = false; + + /** + * Constructor + * + * @param resource &$search Search result identifier + * @param Net_LDAP2|resource &$ldap Net_LDAP2 object or just a LDAP-Link resource + * @param array $attributes (optional) Array with searched attribute names. (see {@link $_searchedAttrs}) + * + * @access public + */ + public function __construct(&$search, &$ldap, $attributes = array()) + { + $this->PEAR('Net_LDAP2_Error'); + + $this->setSearch($search); + + if ($ldap instanceof Net_LDAP2) { + $this->_ldap =& $ldap; + $this->setLink($this->_ldap->getLink()); + } else { + $this->setLink($ldap); + } + + $this->_errorCode = @ldap_errno($this->_link); + + if (is_array($attributes) && !empty($attributes)) { + $this->_searchedAttrs = $attributes; + } + } + + /** + * Returns an array of entry objects + * + * @return array Array of entry objects. + */ + public function entries() + { + $entries = array(); + + while ($entry = $this->shiftEntry()) { + $entries[] = $entry; + } + + return $entries; + } + + /** + * Get the next entry in the searchresult. + * + * This will return a valid Net_LDAP2_Entry object or false, so + * you can use this method to easily iterate over the entries inside + * a while loop. + * + * @return Net_LDAP2_Entry|false Reference to Net_LDAP2_Entry object or false + */ + public function &shiftEntry() + { + if ($this->count() == 0 ) { + $false = false; + return $false; + } + + if (is_null($this->_entry)) { + $this->_entry = @ldap_first_entry($this->_link, $this->_search); + $entry = Net_LDAP2_Entry::createConnected($this->_ldap, $this->_entry); + if ($entry instanceof Net_LDAP2_Error) $entry = false; + } else { + if (!$this->_entry = @ldap_next_entry($this->_link, $this->_entry)) { + $false = false; + return $false; + } + $entry = Net_LDAP2_Entry::createConnected($this->_ldap, $this->_entry); + if ($entry instanceof Net_LDAP2_Error) $entry = false; + } + return $entry; + } + + /** + * Alias function of shiftEntry() for perl-ldap interface + * + * @see shiftEntry() + * @return Net_LDAP2_Entry|false + */ + public function shift_entry() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'shiftEntry' ), $args); + } + + /** + * Retrieve the next entry in the searchresult, but starting from last entry + * + * This is the opposite to {@link shiftEntry()} and is also very useful + * to be used inside a while loop. + * + * @return Net_LDAP2_Entry|false + */ + public function popEntry() + { + if (false === $this->_entry_cache) { + // fetch entries into cache if not done so far + $this->_entry_cache = $this->entries(); + } + + $return = array_pop($this->_entry_cache); + return (null === $return)? false : $return; + } + + /** + * Alias function of popEntry() for perl-ldap interface + * + * @see popEntry() + * @return Net_LDAP2_Entry|false + */ + public function pop_entry() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'popEntry' ), $args); + } + + /** + * Return entries sorted as array + * + * This returns a array with sorted entries and the values. + * Sorting is done with PHPs {@link array_multisort()}. + * This method relies on {@link as_struct()} to fetch the raw data of the entries. + * + * Please note that attribute names are case sensitive! + * + * Usage example: + * + * // to sort entries first by location, then by surename, but descending: + * $entries = $search->sorted_as_struct(array('locality','sn'), SORT_DESC); + * + * + * @param array $attrs Array of attribute names to sort; order from left to right. + * @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC + * + * @return array|Net_LDAP2_Error Array with sorted entries or error + * @todo what about server side sorting as specified in http://www.ietf.org/rfc/rfc2891.txt? + */ + public function sorted_as_struct($attrs = array('cn'), $order = SORT_ASC) + { + /* + * Old Code, suitable and fast for single valued sorting + * This code should be used if we know that single valued sorting is desired, + * but we need some method to get that knowledge... + */ + /* + $attrs = array_reverse($attrs); + foreach ($attrs as $attribute) { + if (!ldap_sort($this->_link, $this->_search, $attribute)){ + $this->raiseError("Sorting failed for Attribute " . $attribute); + } + } + + $results = ldap_get_entries($this->_link, $this->_search); + + unset($results['count']); //for tidier output + if ($order) { + return array_reverse($results); + } else { + return $results; + }*/ + + /* + * New code: complete "client side" sorting + */ + // first some parameterchecks + if (!is_array($attrs)) { + return PEAR::raiseError("Sorting failed: Parameterlist must be an array!"); + } + if ($order != SORT_ASC && $order != SORT_DESC) { + return PEAR::raiseError("Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)"); + } + + // fetch the entries data + $entries = $this->as_struct(); + + // now sort each entries attribute values + // this is neccessary because later we can only sort by one value, + // so we need the highest or lowest attribute now, depending on the + // selected ordering for that specific attribute + foreach ($entries as $dn => $entry) { + foreach ($entry as $attr_name => $attr_values) { + sort($entries[$dn][$attr_name]); + if ($order == SORT_DESC) { + array_reverse($entries[$dn][$attr_name]); + } + } + } + + // reformat entrys array for later use with array_multisort() + $to_sort = array(); // <- will be a numeric array similar to ldap_get_entries + foreach ($entries as $dn => $entry_attr) { + $row = array(); + $row['dn'] = $dn; + foreach ($entry_attr as $attr_name => $attr_values) { + $row[$attr_name] = $attr_values; + } + $to_sort[] = $row; + } + + // Build columns for array_multisort() + // each requested attribute is one row + $columns = array(); + foreach ($attrs as $attr_name) { + foreach ($to_sort as $key => $row) { + $columns[$attr_name][$key] =& $to_sort[$key][$attr_name][0]; + } + } + + // sort the colums with array_multisort, if there is something + // to sort and if we have requested sort columns + if (!empty($to_sort) && !empty($columns)) { + $sort_params = ''; + foreach ($attrs as $attr_name) { + $sort_params .= '$columns[\''.$attr_name.'\'], '.$order.', '; + } + eval("array_multisort($sort_params \$to_sort);"); // perform sorting + } + + return $to_sort; + } + + /** + * Return entries sorted as objects + * + * This returns a array with sorted Net_LDAP2_Entry objects. + * The sorting is actually done with {@link sorted_as_struct()}. + * + * Please note that attribute names are case sensitive! + * Also note, that it is (depending on server capabilitys) possible to let + * the server sort your results. This happens through search controls + * and is described in detail at {@link http://www.ietf.org/rfc/rfc2891.txt} + * + * Usage example: + * + * // to sort entries first by location, then by surename, but descending: + * $entries = $search->sorted(array('locality','sn'), SORT_DESC); + * + * + * @param array $attrs Array of sort attributes to sort; order from left to right. + * @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC + * + * @return array|Net_LDAP2_Error Array with sorted Net_LDAP2_Entries or error + * @todo Entry object construction could be faster. Maybe we could use one of the factorys instead of fetching the entry again + */ + public function sorted($attrs = array('cn'), $order = SORT_ASC) + { + $return = array(); + $sorted = $this->sorted_as_struct($attrs, $order); + if (PEAR::isError($sorted)) { + return $sorted; + } + foreach ($sorted as $key => $row) { + $entry = $this->_ldap->getEntry($row['dn'], $this->searchedAttrs()); + if (!PEAR::isError($entry)) { + array_push($return, $entry); + } else { + return $entry; + } + } + return $return; + } + + /** + * Return entries as array + * + * This method returns the entries and the selected attributes values as + * array. + * The first array level contains all found entries where the keys are the + * DNs of the entries. The second level arrays contian the entries attributes + * such that the keys is the lowercased name of the attribute and the values + * are stored in another indexed array. Note that the attribute values are stored + * in an array even if there is no or just one value. + * + * The array has the following structure: + * + * $return = array( + * 'cn=foo,dc=example,dc=com' => array( + * 'sn' => array('foo'), + * 'multival' => array('val1', 'val2', 'valN') + * ) + * 'cn=bar,dc=example,dc=com' => array( + * 'sn' => array('bar'), + * 'multival' => array('val1', 'valN') + * ) + * ) + * + * + * @return array associative result array as described above + */ + public function as_struct() + { + $return = array(); + $entries = $this->entries(); + foreach ($entries as $entry) { + $attrs = array(); + $entry_attributes = $entry->attributes(); + foreach ($entry_attributes as $attr_name) { + $attr_values = $entry->getValue($attr_name, 'all'); + if (!is_array($attr_values)) { + $attr_values = array($attr_values); + } + $attrs[$attr_name] = $attr_values; + } + $return[$entry->dn()] = $attrs; + } + return $return; + } + + /** + * Set the search objects resource link + * + * @param resource &$search Search result identifier + * + * @access public + * @return void + */ + public function setSearch(&$search) + { + $this->_search = $search; + } + + /** + * Set the ldap ressource link + * + * @param resource &$link Link identifier + * + * @access public + * @return void + */ + public function setLink(&$link) + { + $this->_link = $link; + } + + /** + * Returns the number of entries in the searchresult + * + * @return int Number of entries in search. + */ + public function count() + { + // this catches the situation where OL returned errno 32 = no such object! + if (!$this->_search) { + return 0; + } + return @ldap_count_entries($this->_link, $this->_search); + } + + /** + * Get the errorcode the object got in its search. + * + * @return int The ldap error number. + */ + public function getErrorCode() + { + return $this->_errorCode; + } + + /** + * Destructor + * + * @access protected + */ + public function _Net_LDAP2_Search() + { + @ldap_free_result($this->_search); + } + + /** + * Closes search result + * + * @return void + */ + public function done() + { + $this->_Net_LDAP2_Search(); + } + + /** + * Return the attribute names this search selected + * + * @return array + * @see $_searchedAttrs + * @access protected + */ + protected function searchedAttrs() + { + return $this->_searchedAttrs; + } + + /** + * Tells if this search exceeds a sizelimit + * + * @return boolean + */ + public function sizeLimitExceeded() + { + return ($this->getErrorCode() == 4); + } + + + /* + * SPL Iterator interface methods. + * This interface allows to use Net_LDAP2_Search + * objects directly inside a foreach loop! + */ + /** + * SPL Iterator interface: Return the current element. + * + * The SPL Iterator interface allows you to fetch entries inside + * a foreach() loop: foreach ($search as $dn => $entry) { ... + * + * Of course, you may call {@link current()}, {@link key()}, {@link next()}, + * {@link rewind()} and {@link valid()} yourself. + * + * If the search throwed an error, it returns false. + * False is also returned, if the end is reached + * In case no call to next() was made, we will issue one, + * thus returning the first entry. + * + * @return Net_LDAP2_Entry|false + */ + public function current() + { + if (count($this->_iteratorCache) == 0) { + $this->next(); + reset($this->_iteratorCache); + } + $entry = current($this->_iteratorCache); + return ($entry instanceof Net_LDAP2_Entry)? $entry : false; + } + + /** + * SPL Iterator interface: Return the identifying key (DN) of the current entry. + * + * @see current() + * @return string|false DN of the current entry; false in case no entry is returned by current() + */ + public function key() + { + $entry = $this->current(); + return ($entry instanceof Net_LDAP2_Entry)? $entry->dn() :false; + } + + /** + * SPL Iterator interface: Move forward to next entry. + * + * After a call to {@link next()}, {@link current()} will return + * the next entry in the result set. + * + * @see current() + * @return void + */ + public function next() + { + // fetch next entry. + // if we have no entrys anymore, we add false (which is + // returned by shiftEntry()) so current() will complain. + if (count($this->_iteratorCache) - 1 <= $this->count()) { + $this->_iteratorCache[] = $this->shiftEntry(); + } + + // move on array pointer to current element. + // even if we have added all entries, this will + // ensure proper operation in case we rewind() + next($this->_iteratorCache); + } + + /** + * SPL Iterator interface: Check if there is a current element after calls to {@link rewind()} or {@link next()}. + * + * Used to check if we've iterated to the end of the collection. + * + * @see current() + * @return boolean FALSE if there's nothing more to iterate over + */ + public function valid() + { + return ($this->current() instanceof Net_LDAP2_Entry); + } + + /** + * SPL Iterator interface: Rewind the Iterator to the first element. + * + * After rewinding, {@link current()} will return the first entry in the result set. + * + * @see current() + * @return void + */ + public function rewind() + { + reset($this->_iteratorCache); + } +} + +?> diff --git a/thirdparty/pear/Net/LDAP2/SimpleFileSchemaCache.php b/thirdparty/pear/Net/LDAP2/SimpleFileSchemaCache.php new file mode 100644 index 0000000..8019654 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/SimpleFileSchemaCache.php @@ -0,0 +1,97 @@ + +* @copyright 2009 Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: SimpleFileSchemaCache.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* A simple file based schema cacher with cache aging. +* +* Once the cache is too old, the loadSchema() method will return false, so +* Net_LDAP2 will fetch a fresh object from the LDAP server that will +* overwrite the current (outdated) old cache. +*/ +class Net_LDAP2_SimpleFileSchemaCache implements Net_LDAP2_SchemaCache +{ + /** + * Internal config of this cache + * + * @see Net_LDAP2_SimpleFileSchemaCache() + * @var array + */ + protected $config = array( + 'path' => '/tmp/Net_LDAP_Schema.cache', + 'max_age' => 1200 + ); + + /** + * Initialize the simple cache + * + * Config is as following: + * path Complete path to the cache file. + * max_age Maximum age of cache in seconds, 0 means "endlessly". + * + * @param array $cfg Config array + */ + public function Net_LDAP2_SimpleFileSchemaCache($cfg) + { + foreach ($cfg as $key => $value) { + if (array_key_exists($key, $this->config)) { + if (gettype($this->config[$key]) != gettype($value)) { + $this->getCore()->dropFatalError(__CLASS__.": Could not set config! Key $key does not match type ".gettype($this->config[$key])."!"); + } + $this->config[$key] = $value; + } else { + $this->getCore()->dropFatalError(__CLASS__.": Could not set config! Key $key is not defined!"); + } + } + } + + /** + * Return the schema object from the cache + * + * If file is existent and cache has not expired yet, + * then the cache is deserialized and returned. + * + * @return Net_LDAP2_Schema|Net_LDAP2_Error|false + */ + public function loadSchema() + { + $return = false; // Net_LDAP2 will load schema from LDAP + if (file_exists($this->config['path'])) { + $cache_maxage = filemtime($this->config['path']) + $this->config['max_age']; + if (time() <= $cache_maxage || $this->config['max_age'] == 0) { + $return = unserialize(file_get_contents($this->config['path'])); + } + } + return $return; + } + + /** + * Store a schema object in the cache + * + * This method will be called, if Net_LDAP2 has fetched a fresh + * schema object from LDAP and wants to init or refresh the cache. + * + * To invalidate the cache and cause Net_LDAP2 to refresh the cache, + * you can call this method with null or false as value. + * The next call to $ldap->schema() will then refresh the caches object. + * + * @param mixed $schema The object that should be cached + * @return true|Net_LDAP2_Error|false + */ + public function storeSchema($schema) { + file_put_contents($this->config['path'], serialize($schema)); + return true; + } +} diff --git a/thirdparty/pear/Net/LDAP2/Util.php b/thirdparty/pear/Net/LDAP2/Util.php new file mode 100644 index 0000000..48b03f9 --- /dev/null +++ b/thirdparty/pear/Net/LDAP2/Util.php @@ -0,0 +1,572 @@ + +* @copyright 2009 Benedikt Hallinger +* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 +* @version SVN: $Id: Util.php 286718 2009-08-03 07:30:49Z beni $ +* @link http://pear.php.net/package/Net_LDAP2/ +*/ + +/** +* Includes +*/ +require_once 'PEAR.php'; + +/** +* Utility Class for Net_LDAP2 +* +* This class servers some functionality to the other classes of Net_LDAP2 but most of +* the methods can be used separately as well. +* +* @category Net +* @package Net_LDAP2 +* @author Benedikt Hallinger +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* @link http://pear.php.net/package/Net_LDAP22/ +*/ +class Net_LDAP2_Util extends PEAR +{ + /** + * Constructor + * + * @access public + */ + public function __construct() + { + // We do nothing here, since all methods can be called statically. + // In Net_LDAP <= 0.7, we needed a instance of Util, because + // it was possible to do utf8 encoding and decoding, but this + // has been moved to the LDAP class. The constructor remains only + // here to document the downward compatibility of creating an instance. + } + + /** + * Explodes the given DN into its elements + * + * {@link http://www.ietf.org/rfc/rfc2253.txt RFC 2253} says, a Distinguished Name is a sequence + * of Relative Distinguished Names (RDNs), which themselves + * are sets of Attributes. For each RDN a array is constructed where the RDN part is stored. + * + * For example, the DN 'OU=Sales+CN=J. Smith,DC=example,DC=net' is exploded to: + * array( [0] => array([0] => 'OU=Sales', [1] => 'CN=J. Smith'), [2] => 'DC=example', [3] => 'DC=net' ) + * + * [NOT IMPLEMENTED] DNs might also contain values, which are the bytes of the BER encoding of + * the X.500 AttributeValue rather than some LDAP string syntax. These values are hex-encoded + * and prefixed with a #. To distinguish such BER values, ldap_explode_dn uses references to + * the actual values, e.g. '1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com' is exploded to: + * [ { '1.3.6.1.4.1.1466.0' => "\004\002Hi" }, { 'DC' => 'example' }, { 'DC' => 'com' } ]; + * See {@link http://www.vijaymukhi.com/vmis/berldap.htm} for more information on BER. + * + * It also performs the following operations on the given DN: + * - Unescape "\" followed by ",", "+", """, "\", "<", ">", ";", "#", "=", " ", or a hexpair + * and strings beginning with "#". + * - Removes the leading 'OID.' characters if the type is an OID instead of a name. + * - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order. + * + * OPTIONS is a list of name/value pairs, valid options are: + * casefold Controls case folding of attribute types names. + * Attribute values are not affected by this option. + * The default is to uppercase. Valid values are: + * lower Lowercase attribute types names. + * upper Uppercase attribute type names. This is the default. + * none Do not change attribute type names. + * reverse If TRUE, the RDN sequence is reversed. + * onlyvalues If TRUE, then only attributes values are returned ('foo' instead of 'cn=foo') + * + + * @param string $dn The DN that should be exploded + * @param array $options Options to use + * + * @static + * @return array Parts of the exploded DN + * @todo implement BER + */ + public static function ldap_explode_dn($dn, $options = array('casefold' => 'upper')) + { + if (!isset($options['onlyvalues'])) $options['onlyvalues'] = false; + if (!isset($options['reverse'])) $options['reverse'] = false; + if (!isset($options['casefold'])) $options['casefold'] = 'upper'; + + // Escaping of DN and stripping of "OID." + $dn = self::canonical_dn($dn, array('casefold' => $options['casefold'])); + + // splitting the DN + $dn_array = preg_split('/(?<=[^\\\\]),/', $dn); + + // clear wrong splitting (possibly we have split too much) + // /!\ Not clear, if this is neccessary here + //$dn_array = self::correct_dn_splitting($dn_array, ','); + + // construct subarrays for multivalued RDNs and unescape DN value + // also convert to output format and apply casefolding + foreach ($dn_array as $key => $value) { + $value_u = self::unescape_dn_value($value); + $rdns = self::split_rdn_multival($value_u[0]); + if (count($rdns) > 1) { + // MV RDN! + foreach ($rdns as $subrdn_k => $subrdn_v) { + // Casefolding + if ($options['casefold'] == 'upper') $subrdn_v = preg_replace("/^(\w+=)/e", "''.strtoupper('\\1').''", $subrdn_v); + if ($options['casefold'] == 'lower') $subrdn_v = preg_replace("/^(\w+=)/e", "''.strtolower('\\1').''", $subrdn_v); + + if ($options['onlyvalues']) { + preg_match('/(.+?)(?", ";", "#", "=" with a special meaning in RFC 2252 + * are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair. + * Finally all leading and trailing spaces are converted to sequences of \20. + * + * @param array $values An array containing the DN values that should be escaped + * + * @static + * @return array The array $values, but escaped + */ + public static function escape_dn_value($values = array()) + { + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $val) { + // Escaping of filter meta characters + $val = str_replace('\\', '\\\\', $val); + $val = str_replace(',', '\,', $val); + $val = str_replace('+', '\+', $val); + $val = str_replace('"', '\"', $val); + $val = str_replace('<', '\<', $val); + $val = str_replace('>', '\>', $val); + $val = str_replace(';', '\;', $val); + $val = str_replace('#', '\#', $val); + $val = str_replace('=', '\=', $val); + + // ASCII < 32 escaping + $val = self::asc2hex32($val); + + // Convert all leading and trailing spaces to sequences of \20. + if (preg_match('/^(\s*)(.+?)(\s*)$/', $val, $matches)) { + $val = $matches[2]; + for ($i = 0; $i < strlen($matches[1]); $i++) { + $val = '\20'.$val; + } + for ($i = 0; $i < strlen($matches[3]); $i++) { + $val = $val.'\20'; + } + } + + if (null === $val) $val = '\0'; // apply escaped "null" if string is empty + + $values[$key] = $val; + } + + return $values; + } + + /** + * Undoes the conversion done by escape_dn_value(). + * + * Any escape sequence starting with a baskslash - hexpair or special character - + * will be transformed back to the corresponding character. + * + * @param array $values Array of DN Values + * + * @return array Same as $values, but unescaped + * @static + */ + public static function unescape_dn_value($values = array()) + { + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $val) { + // strip slashes from special chars + $val = str_replace('\\\\', '\\', $val); + $val = str_replace('\,', ',', $val); + $val = str_replace('\+', '+', $val); + $val = str_replace('\"', '"', $val); + $val = str_replace('\<', '<', $val); + $val = str_replace('\>', '>', $val); + $val = str_replace('\;', ';', $val); + $val = str_replace('\#', '#', $val); + $val = str_replace('\=', '=', $val); + + // Translate hex code into ascii + $values[$key] = self::hex2asc($val); + } + + return $values; + } + + /** + * Returns the given DN in a canonical form + * + * Returns false if DN is not a valid Distinguished Name. + * DN can either be a string or an array + * as returned by ldap_explode_dn, which is useful when constructing a DN. + * The DN array may have be indexed (each array value is a OCL=VALUE pair) + * or associative (array key is OCL and value is VALUE). + * + * It performs the following operations on the given DN: + * - Removes the leading 'OID.' characters if the type is an OID instead of a name. + * - Escapes all RFC 2253 special characters (",", "+", """, "\", "<", ">", ";", "#", "="), slashes ("/"), and any other character where the ASCII code is < 32 as \hexpair. + * - Converts all leading and trailing spaces in values to be \20. + * - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order. + * + * OPTIONS is a list of name/value pairs, valid options are: + * casefold Controls case folding of attribute type names. + * Attribute values are not affected by this option. The default is to uppercase. + * Valid values are: + * lower Lowercase attribute type names. + * upper Uppercase attribute type names. This is the default. + * none Do not change attribute type names. + * [NOT IMPLEMENTED] mbcescape If TRUE, characters that are encoded as a multi-octet UTF-8 sequence will be escaped as \(hexpair){2,*}. + * reverse If TRUE, the RDN sequence is reversed. + * separator Separator to use between RDNs. Defaults to comma (','). + * + * Note: The empty string "" is a valid DN, so be sure not to do a "$can_dn == false" test, + * because an empty string evaluates to false. Use the "===" operator instead. + * + * @param array|string $dn The DN + * @param array $options Options to use + * + * @static + * @return false|string The canonical DN or FALSE + * @todo implement option mbcescape + */ + public static function canonical_dn($dn, $options = array('casefold' => 'upper', 'separator' => ',')) + { + if ($dn === '') return $dn; // empty DN is valid! + + // options check + if (!isset($options['reverse'])) { + $options['reverse'] = false; + } else { + $options['reverse'] = true; + } + if (!isset($options['casefold'])) $options['casefold'] = 'upper'; + if (!isset($options['separator'])) $options['separator'] = ','; + + + if (!is_array($dn)) { + // It is not clear to me if the perl implementation splits by the user defined + // separator or if it just uses this separator to construct the new DN + $dn = preg_split('/(?<=[^\\\\])'.$options['separator'].'/', $dn); + + // clear wrong splitting (possibly we have split too much) + $dn = self::correct_dn_splitting($dn, $options['separator']); + } else { + // Is array, check, if the array is indexed or associative + $assoc = false; + foreach ($dn as $dn_key => $dn_part) { + if (!is_int($dn_key)) { + $assoc = true; + } + } + // convert to indexed, if associative array detected + if ($assoc) { + $newdn = array(); + foreach ($dn as $dn_key => $dn_part) { + if (is_array($dn_part)) { + ksort($dn_part, SORT_STRING); // we assume here, that the rdn parts are also associative + $newdn[] = $dn_part; // copy array as-is, so we can resolve it later + } else { + $newdn[] = $dn_key.'='.$dn_part; + } + } + $dn =& $newdn; + } + } + + // Escaping and casefolding + foreach ($dn as $pos => $dnval) { + if (is_array($dnval)) { + // subarray detected, this means very surely, that we had + // a multivalued dn part, which must be resolved + $dnval_new = ''; + foreach ($dnval as $subkey => $subval) { + // build RDN part + if (!is_int($subkey)) { + $subval = $subkey.'='.$subval; + } + $subval_processed = self::canonical_dn($subval); + if (false === $subval_processed) return false; + $dnval_new .= $subval_processed.'+'; + } + $dn[$pos] = substr($dnval_new, 0, -1); // store RDN part, strip last plus + } else { + // try to split multivalued RDNS into array + $rdns = self::split_rdn_multival($dnval); + if (count($rdns) > 1) { + // Multivalued RDN was detected! + // The RDN value is expected to be correctly split by split_rdn_multival(). + // It's time to sort the RDN and build the DN! + $rdn_string = ''; + sort($rdns, SORT_STRING); // Sort RDN keys alphabetically + foreach ($rdns as $rdn) { + $subval_processed = self::canonical_dn($rdn); + if (false === $subval_processed) return false; + $rdn_string .= $subval_processed.'+'; + } + + $dn[$pos] = substr($rdn_string, 0, -1); // store RDN part, strip last plus + + } else { + // no multivalued RDN! + // split at first unescaped "=" + $dn_comp = preg_split('/(?<=[^\\\\])=/', $rdns[0], 2); + $ocl = ltrim($dn_comp[0]); // trim left whitespaces 'cause of "cn=foo, l=bar" syntax (whitespace after comma) + $val = $dn_comp[1]; + + // strip 'OID.', otherwise apply casefolding and escaping + if (substr(strtolower($ocl), 0, 4) == 'oid.') { + $ocl = substr($ocl, 4); + } else { + if ($options['casefold'] == 'upper') $ocl = strtoupper($ocl); + if ($options['casefold'] == 'lower') $ocl = strtolower($ocl); + $ocl = self::escape_dn_value(array($ocl)); + $ocl = $ocl[0]; + } + + // escaping of dn-value + $val = self::escape_dn_value(array($val)); + $val = str_replace('/', '\/', $val[0]); + + $dn[$pos] = $ocl.'='.$val; + } + } + } + + if ($options['reverse']) $dn = array_reverse($dn); + return implode($options['separator'], $dn); + } + + /** + * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters. + * + * Any control characters with an ACII code < 32 as well as the characters with special meaning in + * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a + * backslash followed by two hex digits representing the hexadecimal value of the character. + * + * @param array $values Array of values to escape + * + * @static + * @return array Array $values, but escaped + */ + public static function escape_filter_value($values = array()) + { + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $val) { + // Escaping of filter meta characters + $val = str_replace('\\', '\5c', $val); + $val = str_replace('*', '\2a', $val); + $val = str_replace('(', '\28', $val); + $val = str_replace(')', '\29', $val); + + // ASCII < 32 escaping + $val = self::asc2hex32($val); + + if (null === $val) $val = '\0'; // apply escaped "null" if string is empty + + $values[$key] = $val; + } + + return $values; + } + + /** + * Undoes the conversion done by {@link escape_filter_value()}. + * + * Converts any sequences of a backslash followed by two hex digits into the corresponding character. + * + * @param array $values Array of values to escape + * + * @static + * @return array Array $values, but unescaped + */ + public static function unescape_filter_value($values = array()) + { + // Parameter validation + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $key => $value) { + // Translate hex code into ascii + $values[$key] = self::hex2asc($value); + } + + return $values; + } + + /** + * Converts all ASCII chars < 32 to "\HEX" + * + * @param string $string String to convert + * + * @static + * @return string + */ + public static function asc2hex32($string) + { + for ($i = 0; $i < strlen($string); $i++) { + $char = substr($string, $i, 1); + if (ord($char) < 32) { + $hex = dechex(ord($char)); + if (strlen($hex) == 1) $hex = '0'.$hex; + $string = str_replace($char, '\\'.$hex, $string); + } + } + return $string; + } + + /** + * Converts all Hex expressions ("\HEX") to their original ASCII characters + * + * @param string $string String to convert + * + * @static + * @author beni@php.net, heavily based on work from DavidSmith@byu.net + * @return string + */ + public static function hex2asc($string) + { + $string = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $string); + return $string; + } + + /** + * Split an multivalued RDN value into an Array + * + * A RDN can contain multiple values, spearated by a plus sign. + * This function returns each separate ocl=value pair of the RDN part. + * + * If no multivalued RDN is detected, an array containing only + * the original rdn part is returned. + * + * For example, the multivalued RDN 'OU=Sales+CN=J. Smith' is exploded to: + * array([0] => 'OU=Sales', [1] => 'CN=J. Smith') + * + * The method trys to be smart if it encounters unescaped "+" characters, but may fail, + * so ensure escaped "+"es in attr names and attr values. + * + * [BUG] If you have a multivalued RDN with unescaped plus characters + * and there is a unescaped plus sign at the end of an value followed by an + * attribute name containing an unescaped plus, then you will get wrong splitting: + * $rdn = 'OU=Sales+C+N=J. Smith'; + * returns: + * array('OU=Sales+C', 'N=J. Smith'); + * The "C+" is treaten as value of the first pair instead as attr name of the second pair. + * To prevent this, escape correctly. + * + * @param string $rdn Part of an (multivalued) escaped RDN (eg. ou=foo OR ou=foo+cn=bar) + * + * @static + * @return array Array with the components of the multivalued RDN or Error + */ + public static function split_rdn_multival($rdn) + { + $rdns = preg_split('/(? $dn_value) { + $dn_value = $dn[$key]; // refresh value (foreach caches!) + // if the dn_value is not in attr=value format, then we had an + // unescaped separator character inside the attr name or the value. + // We assume, that it was the attribute value. + // [TODO] To solve this, we might ask the schema. Keep in mind, that UTIL class + // must remain independent from the other classes or connections. + if (!preg_match('/.+(? diff --git a/thirdparty/pear/Net/LMTP.php b/thirdparty/pear/Net/LMTP.php old mode 100644 new mode 100755 index e5fd17a..e5fd17a --- a/thirdparty/pear/Net/LMTP.php +++ b/thirdparty/pear/Net/LMTP.php diff --git a/thirdparty/pear/Net/POP3.php b/thirdparty/pear/Net/POP3.php old mode 100644 new mode 100755 index b73f842..b73f842 --- a/thirdparty/pear/Net/POP3.php +++ b/thirdparty/pear/Net/POP3.php diff --git a/thirdparty/pear/Net/Ping.php b/thirdparty/pear/Net/Ping.php old mode 100644 new mode 100755 index 8446cf5..8446cf5 --- a/thirdparty/pear/Net/Ping.php +++ b/thirdparty/pear/Net/Ping.php diff --git a/thirdparty/pear/Net/SMTP.php b/thirdparty/pear/Net/SMTP.php old mode 100644 new mode 100755 index 4a29f4d..176517e --- a/thirdparty/pear/Net/SMTP.php +++ b/thirdparty/pear/Net/SMTP.php @@ -18,7 +18,7 @@ // | Damian Alejandro Fernandez Sosa | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: SMTP.php 293948 2010-01-24 21:46:00Z jon $ require_once 'PEAR.php'; require_once 'Net/Socket.php'; @@ -36,7 +36,6 @@ require_once 'Net/Socket.php'; */ class Net_SMTP { - /** * The server to connect to. * @var string @@ -66,6 +65,26 @@ class Net_SMTP var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN'); /** + * Use SMTP command pipelining (specified in RFC 2920) if the SMTP + * server supports it. + * + * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(), + * somlFrom() and samlFrom() do not wait for a response from the + * SMTP server but return immediately. + * + * @var bool + * @access public + */ + var $pipelining = false; + + /** + * Number of pipelined commands. + * @var int + * @access private + */ + var $_pipelined_commands = 0; + + /** * Should debugging output be enabled? * @var boolean * @access private @@ -73,6 +92,13 @@ class Net_SMTP var $_debug = false; /** + * Debug output handler. + * @var callback + * @access private + */ + var $_debug_handler = null; + + /** * The socket resource being used to connect to the SMTP server. * @var resource * @access private @@ -94,6 +120,13 @@ class Net_SMTP var $_arguments = array(); /** + * Stores the SMTP server's greeting string. + * @var string + * @access private + */ + var $_greeting = null; + + /** * Stores detected features of the SMTP server. * @var array * @access private @@ -114,22 +147,29 @@ class Net_SMTP * @param string $host The server to connect to. * @param integer $port The port to connect to. * @param string $localhost The value to give when sending EHLO or HELO. + * @param boolean $pipeling Use SMTP command pipelining * * @access public * @since 1.0 */ - function Net_SMTP($host = null, $port = null, $localhost = null) + function Net_SMTP($host = null, $port = null, $localhost = null, $pipelining = false) { - if (isset($host)) $this->host = $host; - if (isset($port)) $this->port = $port; - if (isset($localhost)) $this->localhost = $localhost; + if (isset($host)) { + $this->host = $host; + } + if (isset($port)) { + $this->port = $port; + } + if (isset($localhost)) { + $this->localhost = $localhost; + } + $this->pipelining = $pipelining; - $this->_socket = &new Net_Socket(); + $this->_socket = new Net_Socket(); - /* - * Include the Auth_SASL package. If the package is not available, - * we disable the authentication methods that depend upon it. - */ + /* Include the Auth_SASL package. If the package is not + * available, we disable the authentication methods that + * depend upon it. */ if ((@include_once 'Auth/SASL.php') === false) { $pos = array_search('DIGEST-MD5', $this->auth_methods); unset($this->auth_methods[$pos]); @@ -146,9 +186,30 @@ class Net_SMTP * @access public * @since 1.1.0 */ - function setDebug($debug) + function setDebug($debug, $handler = null) { $this->_debug = $debug; + $this->_debug_handler = $handler; + } + + /** + * Write the given debug text to the current debug output handler. + * + * @param string $message Debug mesage text. + * + * @access private + * @since 1.3.3 + */ + function _debug($message) + { + if ($this->_debug) { + if ($this->_debug_handler) { + call_user_func_array($this->_debug_handler, + array(&$this, $message)); + } else { + echo "DEBUG: $message\n"; + } + } } /** @@ -163,13 +224,12 @@ class Net_SMTP */ function _send($data) { - if ($this->_debug) { - echo "DEBUG: Send: $data\n"; - } + $this->_debug("Send: $data"); - if (PEAR::isError($error = $this->_socket->write($data))) { - return PEAR::raiseError('Failed to write to socket: ' . - $error->getMessage()); + $error = $this->_socket->write($data); + if ($error === false || PEAR::isError($error)) { + $msg = ($error) ? $error->getMessage() : "unknown error"; + return PEAR::raiseError("Failed to write to socket: $msg"); } return true; @@ -212,6 +272,9 @@ class Net_SMTP * @param mixed $valid The set of valid response codes. These * may be specified as an array of integer * values or as a single integer value. + * @param bool $later Do not parse the response now, but wait + * until the last command in the pipelined + * command group * * @return mixed True if the server returned a valid response code or * a PEAR_Error object is an error condition is reached. @@ -221,55 +284,56 @@ class Net_SMTP * * @see getResponse */ - function _parseResponse($valid) + function _parseResponse($valid, $later = false) { $this->_code = -1; $this->_arguments = array(); - while ($line = $this->_socket->readLine()) { - if ($this->_debug) { - echo "DEBUG: Recv: $line\n"; - } + if ($later) { + $this->_pipelined_commands++; + return true; + } - /* If we receive an empty line, the connection has been closed. */ - if (empty($line)) { - $this->disconnect(); - return PEAR::raiseError('Connection was unexpectedly closed'); - } + for ($i = 0; $i <= $this->_pipelined_commands; $i++) { + while ($line = $this->_socket->readLine()) { + $this->_debug("Recv: $line"); - /* Read the code and store the rest in the arguments array. */ - $code = substr($line, 0, 3); - $this->_arguments[] = trim(substr($line, 4)); + /* If we receive an empty line, the connection has been closed. */ + if (empty($line)) { + $this->disconnect(); + return PEAR::raiseError('Connection was unexpectedly closed'); + } - /* Check the syntax of the response code. */ - if (is_numeric($code)) { - $this->_code = (int)$code; - } else { - $this->_code = -1; - break; - } + /* Read the code and store the rest in the arguments array. */ + $code = substr($line, 0, 3); + $this->_arguments[] = trim(substr($line, 4)); + + /* Check the syntax of the response code. */ + if (is_numeric($code)) { + $this->_code = (int)$code; + } else { + $this->_code = -1; + break; + } - /* If this is not a multiline response, we're done. */ - if (substr($line, 3, 1) != '-') { - break; + /* If this is not a multiline response, we're done. */ + if (substr($line, 3, 1) != '-') { + break; + } } } - /* Compare the server's response code with the valid code. */ + $this->_pipelined_commands = 0; + + /* Compare the server's response code with the valid code/codes. */ if (is_int($valid) && ($this->_code === $valid)) { return true; + } elseif (is_array($valid) && in_array($this->_code, $valid, true)) { + return true; } - /* If we were given an array of valid response codes, check each one. */ - if (is_array($valid)) { - foreach ($valid as $valid_code) { - if ($this->_code === $valid_code) { - return true; - } - } - } - - return PEAR::raiseError('Invalid response code received from server'); + return PEAR::raiseError('Invalid response code received from server', + $this->_code); } /** @@ -288,6 +352,20 @@ class Net_SMTP } /** + * Return the SMTP server's greeting string. + * + * @return string A string containing the greeting string, or null if a + * greeting has not been received. + * + * @access public + * @since 1.3.3 + */ + function getGreeting() + { + return $this->_greeting; + } + + /** * Attempt to connect to the SMTP server. * * @param int $timeout The timeout value (in seconds) for the @@ -302,6 +380,7 @@ class Net_SMTP */ function connect($timeout = null, $persistent = false) { + $this->_greeting = null; $result = $this->_socket->connect($this->host, $this->port, $persistent, $timeout); if (PEAR::isError($result)) { @@ -312,6 +391,10 @@ class Net_SMTP if (PEAR::isError($error = $this->_parseResponse(220))) { return $error; } + + /* Extract and store a copy of the server's greeting string. */ + list(, $this->_greeting) = $this->getResponse(); + if (PEAR::isError($error = $this->_negotiate())) { return $error; } @@ -383,6 +466,10 @@ class Net_SMTP $this->_esmtp[$verb] = $arguments; } + if (!isset($this->_esmtp['PIPELINING'])) { + $this->pipelining = false; + } + return true; } @@ -416,16 +503,43 @@ class Net_SMTP * @param string The password to authenticate with. * @param string The requested authentication method. If none is * specified, the best supported method will be used. + * @param bool Flag indicating whether or not TLS should be attempted. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public * @since 1.0 */ - function auth($uid, $pwd , $method = '') + function auth($uid, $pwd , $method = '', $tls = true) { + /* We can only attempt a TLS connection if one has been requested, + * we're running PHP 5.1.0 or later, have access to the OpenSSL + * extension, are connected to an SMTP server which supports the + * STARTTLS extension, and aren't already connected over a secure + * (SSL) socket connection. */ + if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') && + extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) && + strncasecmp($this->host, 'ssl://', 6) !== 0) { + /* Start the TLS connection attempt. */ + if (PEAR::isError($result = $this->_put('STARTTLS'))) { + return $result; + } + if (PEAR::isError($result = $this->_parseResponse(220))) { + return $result; + } + if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) { + return $result; + } elseif ($result !== true) { + return PEAR::raiseError('STARTTLS failed'); + } + + /* Send EHLO again to recieve the AUTH string from the + * SMTP server. */ + $this->_negotiate(); + } + if (empty($this->_esmtp['AUTH'])) { - return PEAR::raiseError('SMTP server does no support authentication'); + return PEAR::raiseError('SMTP server does not support authentication'); } /* If no method has been specified, get the name of the best @@ -443,21 +557,25 @@ class Net_SMTP } switch ($method) { - case 'DIGEST-MD5': - $result = $this->_authDigest_MD5($uid, $pwd); - break; - case 'CRAM-MD5': - $result = $this->_authCRAM_MD5($uid, $pwd); - break; - case 'LOGIN': - $result = $this->_authLogin($uid, $pwd); - break; - case 'PLAIN': - $result = $this->_authPlain($uid, $pwd); - break; - default: - $result = PEAR::raiseError("$method is not a supported authentication method"); - break; + case 'DIGEST-MD5': + $result = $this->_authDigest_MD5($uid, $pwd); + break; + + case 'CRAM-MD5': + $result = $this->_authCRAM_MD5($uid, $pwd); + break; + + case 'LOGIN': + $result = $this->_authLogin($uid, $pwd); + break; + + case 'PLAIN': + $result = $this->_authPlain($uid, $pwd); + break; + + default: + $result = PEAR::raiseError("$method is not a supported authentication method"); + break; } /* If an error was encountered, return the PEAR_Error object. */ @@ -509,7 +627,7 @@ class Net_SMTP /* We don't use the protocol's third step because SMTP doesn't * allow subsequent authentication, so we just silently ignore * it. */ - if (PEAR::isError($error = $this->_put(' '))) { + if (PEAR::isError($error = $this->_put(''))) { return $error; } /* 235: Authentication successful */ @@ -664,39 +782,58 @@ class Net_SMTP } /** + * Return the list of SMTP service extensions advertised by the server. + * + * @return array The list of SMTP service extensions. + * @access public + * @since 1.3 + */ + function getServiceExtensions() + { + return $this->_esmtp; + } + + /** * Send the MAIL FROM: command. * - * @param string The sender (reverse path) to set. + * @param string $sender The sender (reverse path) to set. + * @param string $params String containing additional MAIL parameters, + * such as the NOTIFY flags defined by RFC 1891 + * or the VERP protocol. * - * @param array optional arguments. Currently supported: - * verp boolean or string. If true or string - * verp is enabled. If string the characters - * are considered verp separators. + * If $params is an array, only the 'verp' option + * is supported. If 'verp' is true, the XVERP + * parameter is appended to the MAIL command. If + * the 'verp' value is a string, the full + * XVERP=value parameter is appended. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public * @since 1.0 */ - function mailFrom($sender, $args = array()) + function mailFrom($sender, $params = null) { - $argstr = ''; + $args = "FROM:<$sender>"; - if (isset($args['verp'])) { + /* Support the deprecated array form of $params. */ + if (is_array($params) && isset($params['verp'])) { /* XVERP */ - if ($args['verp'] === true) { - $argstr .= ' XVERP'; + if ($params['verp'] === true) { + $args .= ' XVERP'; /* XVERP=something */ - } elseif (trim($args['verp'])) { - $argstr .= ' XVERP=' . $args['verp']; + } elseif (trim($params['verp'])) { + $args .= ' XVERP=' . $params['verp']; } + } elseif (is_string($params)) { + $args .= ' ' . $params; } - if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>$argstr"))) { + if (PEAR::isError($error = $this->_put('MAIL', $args))) { return $error; } - if (PEAR::isError($error = $this->_parseResponse(250))) { + if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { return $error; } @@ -706,19 +843,27 @@ class Net_SMTP /** * Send the RCPT TO: command. * - * @param string The recipient (forward path) to add. + * @param string $recipient The recipient (forward path) to add. + * @param string $params String containing additional RCPT parameters, + * such as the NOTIFY flags defined by RFC 1891. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. + * * @access public * @since 1.0 */ - function rcptTo($recipient) + function rcptTo($recipient, $params = null) { - if (PEAR::isError($error = $this->_put('RCPT', "TO:<$recipient>"))) { + $args = "TO:<$recipient>"; + if (is_string($params)) { + $args .= ' ' . $params; + } + + if (PEAR::isError($error = $this->_put('RCPT', $args))) { return $error; } - if (PEAR::isError($error = $this->_parseResponse(array(250, 251)))) { + if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) { return $error; } @@ -753,30 +898,51 @@ class Net_SMTP /** * Send the DATA command. * - * @param string $data The message body to send. + * @param mixed $data The message data, either as a string or an open + * file resource. + * @param string $headers The message headers. If $headers is provided, + * $data is assumed to contain only body data. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public * @since 1.0 */ - function data($data) + function data($data, $headers = null) { + /* Verify that $data is a supported type. */ + if (!is_string($data) && !is_resource($data)) { + return PEAR::raiseError('Expected a string or file resource'); + } + /* RFC 1870, section 3, subsection 3 states "a value of zero * indicates that no fixed maximum message size is in force". * Furthermore, it says that if "the parameter is omitted no * information is conveyed about the server's fixed maximum * message size". */ if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) { - if (strlen($data) >= $this->_esmtp['SIZE']) { + /* Start by considering the size of the optional headers string. + * We also account for the addition 4 character "\r\n\r\n" + * separator sequence. */ + $size = (is_null($headers)) ? 0 : strlen($headers) + 4; + + if (is_resource($data)) { + $stat = fstat($data); + if ($stat === false) { + return PEAR::raiseError('Failed to get file size'); + } + $size += $stat['size']; + } else { + $size += strlen($data); + } + + if ($size >= $this->_esmtp['SIZE']) { $this->disconnect(); - return PEAR::raiseError('Message size excedes the server limit'); + return PEAR::raiseError('Message size exceeds server limit'); } } - /* Quote the data based on the SMTP standards. */ - $this->quotedata($data); - + /* Initiate the DATA command. */ if (PEAR::isError($error = $this->_put('DATA'))) { return $error; } @@ -784,10 +950,41 @@ class Net_SMTP return $error; } - if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) { - return $result; + /* If we have a separate headers string, send it first. */ + if (!is_null($headers)) { + $this->quotedata($line); + if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) { + return $result; + } } - if (PEAR::isError($error = $this->_parseResponse(250))) { + + /* Now we can send the message body data. */ + if (is_resource($data)) { + /* Stream the contents of the file resource out over our socket + * connection, line by line. Each line must be run through the + * quoting routine. */ + while ($line = fgets($data, 1024)) { + $this->quotedata($line); + if (PEAR::isError($result = $this->_send($line))) { + return $result; + } + } + + /* Finally, send the DATA terminator sequence. */ + if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) { + return $result; + } + } else { + /* Just send the entire quoted string followed by the DATA + * terminator. */ + $this->quotedata($data); + if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) { + return $result; + } + } + + /* Verify that the data was successfully received by the server. */ + if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { return $error; } @@ -809,7 +1006,7 @@ class Net_SMTP if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) { return $error; } - if (PEAR::isError($error = $this->_parseResponse(250))) { + if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { return $error; } @@ -848,7 +1045,7 @@ class Net_SMTP if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) { return $error; } - if (PEAR::isError($error = $this->_parseResponse(250))) { + if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { return $error; } @@ -887,7 +1084,7 @@ class Net_SMTP if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) { return $error; } - if (PEAR::isError($error = $this->_parseResponse(250))) { + if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { return $error; } @@ -924,7 +1121,7 @@ class Net_SMTP if (PEAR::isError($error = $this->_put('RSET'))) { return $error; } - if (PEAR::isError($error = $this->_parseResponse(250))) { + if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { return $error; } diff --git a/thirdparty/pear/Net/Sieve.php b/thirdparty/pear/Net/Sieve.php old mode 100644 new mode 100755 index aefd60d..aefd60d --- a/thirdparty/pear/Net/Sieve.php +++ b/thirdparty/pear/Net/Sieve.php diff --git a/thirdparty/pear/Net/SmartIRC.php b/thirdparty/pear/Net/SmartIRC.php old mode 100644 new mode 100755 index 95e2b50..95e2b50 --- a/thirdparty/pear/Net/SmartIRC.php +++ b/thirdparty/pear/Net/SmartIRC.php diff --git a/thirdparty/pear/Net/Socket.php b/thirdparty/pear/Net/Socket.php old mode 100644 new mode 100755 index 65f368d..73bb4dd --- a/thirdparty/pear/Net/Socket.php +++ b/thirdparty/pear/Net/Socket.php @@ -17,13 +17,13 @@ // | Chuck Hagenbuch | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: Socket.php,v 1.38 2008/02/15 18:24:17 chagenbu Exp $ require_once 'PEAR.php'; define('NET_SOCKET_READ', 1); define('NET_SOCKET_WRITE', 2); -define('NET_SOCKET_ERROR', 3); +define('NET_SOCKET_ERROR', 4); /** * Generalized Socket class. @@ -123,6 +123,7 @@ class Net_Socket extends PEAR { $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen'; $errno = 0; $errstr = ''; + $old_track_errors = @ini_set('track_errors', 1); if ($options && function_exists('stream_context_create')) { if ($this->timeout) { $timeout = $this->timeout; @@ -130,7 +131,15 @@ class Net_Socket extends PEAR { $timeout = 0; } $context = stream_context_create($options); - $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context); + + // Since PHP 5 fsockopen doesn't allow context specification + if (function_exists('stream_socket_client')) { + $flags = $this->persistent ? STREAM_CLIENT_PERSISTENT : STREAM_CLIENT_CONNECT; + $addr = $this->addr . ':' . $this->port; + $fp = stream_socket_client($addr, $errno, $errstr, $timeout, $flags, $context); + } else { + $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context); + } } else { if ($this->timeout) { $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout); @@ -140,9 +149,14 @@ class Net_Socket extends PEAR { } if (!$fp) { + if ($errno == 0 && isset($php_errormsg)) { + $errstr = $php_errormsg; + } + @ini_set('track_errors', $old_track_errors); return $this->raiseError($errstr, $errno); } + @ini_set('track_errors', $old_track_errors); $this->fp = $fp; return $this->setBlocking($this->blocking); @@ -152,7 +166,7 @@ class Net_Socket extends PEAR { * Disconnects from the peer, closes the socket. * * @access public - * @return mixed true on success or an error object otherwise + * @return mixed true on success or a PEAR_Error instance otherwise */ function disconnect() { @@ -184,7 +198,7 @@ class Net_Socket extends PEAR { * * @param boolean $mode True for blocking sockets, false for nonblocking. * @access public - * @return mixed true on success or an error object otherwise + * @return mixed true on success or a PEAR_Error instance otherwise */ function setBlocking($mode) { @@ -204,7 +218,7 @@ class Net_Socket extends PEAR { * @param integer $seconds Seconds. * @param integer $microseconds Microseconds. * @access public - * @return mixed true on success or an error object otherwise + * @return mixed true on success or a PEAR_Error instance otherwise */ function setTimeout($seconds, $microseconds) { @@ -216,6 +230,27 @@ class Net_Socket extends PEAR { } /** + * Sets the file buffering size on the stream. + * See php's stream_set_write_buffer for more information. + * + * @param integer $size Write buffer size. + * @access public + * @return mixed on success or an PEAR_Error object otherwise + */ + function setWriteBuffer($size) + { + if (!is_resource($this->fp)) { + return $this->raiseError('not connected'); + } + + $returned = stream_set_write_buffer($this->fp, $size); + if ($returned == 0) { + return true; + } + return $this->raiseError('Cannot set write buffer.'); + } + + /** * Returns information about an existing socket resource. * Currently returns four entries in the result array: * @@ -227,7 +262,7 @@ class Net_Socket extends PEAR { *

    * * @access public - * @return mixed Array containing information about existing socket resource or an error object otherwise + * @return mixed Array containing information about existing socket resource or a PEAR_Error instance otherwise */ function getStatus() { @@ -282,7 +317,9 @@ class Net_Socket extends PEAR { * NULL means all at once. * * @access public - * @return mixed true on success or an error object otherwise + * @return mixed If the socket is not connected, returns an instance of PEAR_Error + * If the write succeeds, returns the number of bytes written + * If the write fails, returns false. */ function write($data, $blocksize = null) { @@ -291,7 +328,7 @@ class Net_Socket extends PEAR { } if (is_null($blocksize) && !OS_WINDOWS) { - return fwrite($this->fp, $data); + return @fwrite($this->fp, $data); } else { if (is_null($blocksize)) { $blocksize = 1024; @@ -329,12 +366,14 @@ class Net_Socket extends PEAR { /** * Tests for end-of-file on a socket descriptor. * + * Also returns true if the socket is disconnected. + * * @access public * @return bool */ function eof() { - return (is_resource($this->fp) && feof($this->fp)); + return (!is_resource($this->fp) || feof($this->fp)); } /** @@ -409,10 +448,10 @@ class Net_Socket extends PEAR { } /** - * Reads an IP Address and returns it in a dot formated string + * Reads an IP Address and returns it in a dot formatted string * * @access public - * @return Dot formated string, or a PEAR_Error if + * @return Dot formatted string, or a PEAR_Error if * not connected. */ function readIPAddress() @@ -422,7 +461,7 @@ class Net_Socket extends PEAR { } $buf = @fread($this->fp, 4); - return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]), + return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]), ord($buf[2]), ord($buf[3])); } @@ -525,4 +564,29 @@ class Net_Socket extends PEAR { return $result; } + /** + * Turns encryption on/off on a connected socket. + * + * @param bool $enabled Set this parameter to true to enable encryption + * and false to disable encryption. + * @param integer $type Type of encryption. See + * http://se.php.net/manual/en/function.stream-socket-enable-crypto.php for values. + * + * @access public + * @return false on error, true on success and 0 if there isn't enough data and the + * user should try again (non-blocking sockets only). A PEAR_Error object + * is returned if the socket is not connected + */ + function enableCrypto($enabled, $type) + { + if (version_compare(phpversion(), "5.1.0", ">=")) { + if (!is_resource($this->fp)) { + return $this->raiseError('not connected'); + } + return @stream_socket_enable_crypto($this->fp, $enabled, $type); + } else { + return $this->raiseError('Net_Socket::enableCrypto() requires php version >= 5.1.0'); + } + } + } diff --git a/thirdparty/pear/Net/URL.php b/thirdparty/pear/Net/URL.php old mode 100644 new mode 100755 index 6f8790e..5598b84 --- a/thirdparty/pear/Net/URL.php +++ b/thirdparty/pear/Net/URL.php @@ -32,12 +32,14 @@ // | Author: Richard Heyes | // +-----------------------------------------------------------------------+ // -// $Id$ +// $Id: URL.php,v 1.49 2007/06/28 14:43:07 davidc Exp $ // // Net_URL Class + class Net_URL { + var $options = array('encode_query_keys' => false); /** * Full url * @var string @@ -104,7 +106,8 @@ class Net_URL * @see __construct() */ function Net_URL($url = null, $useBrackets = true) - { + { + $this->__construct($url, $useBrackets); } @@ -121,10 +124,16 @@ class Net_URL */ function __construct($url = null, $useBrackets = true) { + $this->url = $url; + $this->useBrackets = $useBrackets; + + $this->initialize(); + } + + function initialize() + { $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS']; - $this->useBrackets = $useBrackets; - $this->url = $url; $this->user = ''; $this->pass = ''; $this->host = ''; @@ -134,14 +143,15 @@ class Net_URL $this->anchor = ''; // Only use defaults if not an absolute URL given - if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) { - - $this->protocol = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http'); + if (!preg_match('/^[a-z0-9]+:\/\//i', $this->url)) { + $this->protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http'); /** * Figure out host/port */ - if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) { + if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) && + preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) + { $host = $matches[1]; if (!empty($matches[3])) { $port = $matches[3]; @@ -160,8 +170,8 @@ class Net_URL } // Parse the url and store the various parts - if (!empty($url)) { - $urlinfo = parse_url($url); + if (!empty($this->url)) { + $urlinfo = parse_url($this->url); // Default querystring $this->querystring = array(); @@ -200,7 +210,6 @@ class Net_URL } } } - /** * Returns full url * @@ -223,7 +232,11 @@ class Net_URL } /** - * Adds a querystring item + * Adds or updates a querystring item (URL parameter). + * Automatically encodes parameters with rawurlencode() if $preencoded + * is false. + * You can pass an array to $value, it gets mapped via [] in the URL if + * $this->useBrackets is activated. * * @param string $name Name of item * @param string $value Value of item @@ -232,6 +245,10 @@ class Net_URL */ function addQueryString($name, $value, $preencoded = false) { + if ($this->getOption('encode_query_keys')) { + $name = rawurlencode($name); + } + if ($preencoded) { $this->querystring[$name] = $value; } else { @@ -247,6 +264,10 @@ class Net_URL */ function removeQueryString($name) { + if ($this->getOption('encode_query_keys')) { + $name = rawurlencode($name); + } + if (isset($this->querystring[$name])) { unset($this->querystring[$name]); } @@ -273,6 +294,9 @@ class Net_URL { if (!empty($this->querystring)) { foreach ($this->querystring as $name => $value) { + // Encode var name + $name = rawurlencode($name); + if (is_array($value)) { foreach ($value as $k => $v) { $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v); @@ -311,13 +335,27 @@ class Net_URL $value = null; $key = $part; } - if (substr($key, -2) == '[]') { - $key = substr($key, 0, -2); - if (@!is_array($return[$key])) { - $return[$key] = array(); + + //$encodekeys = $this->getOption('encode_query_keys'); // This method fails in KnowledgeTree (Gives Bad Pointer Refs) + $encodekeys = $this->options['encode_query_keys']; + if (!$encodekeys) { + $key = rawurldecode($key); + } + + if (preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) { + $key = $matches[1]; + $idx = $matches[2]; + + // Ensure is an array + if (empty($return[$key]) || !is_array($return[$key])) { + $return[$key] = array(); + } + + // Add data + if ($idx === '') { $return[$key][] = $value; } else { - $return[$key][] = $value; + $return[$key][$idx] = $value; } } elseif (!$this->useBrackets AND !empty($return[$key])) { $return[$key] = (array)$return[$key]; @@ -327,6 +365,21 @@ class Net_URL } } + /* //TODO: when 'encode_query_keys' option needs to be implemented re open this + * to test. + * 1. Add Ldap Authentication Source in Administration->Users and Groups->Authentication + * 2. Edit Provider Configuration. + * 3. Save generates an error that can be caught here: + */ + /* + if ($querystring == 'source_id=1') { + var_dump($this->options); + + //var_dump($return); + exit; + } + */ + return $return; } @@ -340,7 +393,7 @@ class Net_URL * * This method can also be called statically. * - * @param string $url URL path to resolve + * @param string $path URL path to resolve * @return string The result */ function resolvePath($path) @@ -403,7 +456,47 @@ class Net_URL function setProtocol($protocol, $port = null) { $this->protocol = $protocol; - $this->port = is_null($port) ? $this->getStandardPort($protocol) : $port; + $this->port = is_null($port) ? $this->getStandardPort($protocol) : $port; + } + + /** + * Set an option + * + * This function set an option + * to be used thorough the script. + * + * @access public + * @param string $optionName The optionname to set + * @param string $value The value of this option. + */ + function setOption($optionName, $value) + { + if (!array_key_exists($optionName, $this->options)) { + return false; + } + + $this->options[$optionName] = $value; + $this->initialize(); + } + + /** + * Get an option + * + * This function gets an option + * from the $this->options array + * and return it's value. + * + * @access public + * @param string $opionName The name of the option to retrieve + * @see $this->options + */ + function getOption($optionName) + { + if (!isset($this->options[$optionName])) { + return false; + } + + return $this->options[$optionName]; } } diff --git a/thirdparty/pear/Net/Whois.php b/thirdparty/pear/Net/Whois.php old mode 100644 new mode 100755 index 5550f91..5550f91 --- a/thirdparty/pear/Net/Whois.php +++ b/thirdparty/pear/Net/Whois.php diff --git a/thirdparty/pear/PEAR.php b/thirdparty/pear/PEAR.php old mode 100644 new mode 100755 index d48c9fb..d48ee82 --- a/thirdparty/pear/PEAR.php +++ b/thirdparty/pear/PEAR.php @@ -1,26 +1,27 @@ | -// | Stig Bakken | -// | Tomas V.V.Cox | -// +--------------------------------------------------------------------+ -// -// $Id$ -// +/** + * PEAR, the PHP Extension and Application Repository + * + * PEAR class and PEAR_Error class + * + * PHP versions 4 and 5 + * + * @category pear + * @package PEAR + * @author Sterling Hughes + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: PEAR.php 286670 2009-08-02 14:16:06Z dufuz $ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ +/**#@+ + * ERROR constants + */ define('PEAR_ERROR_RETURN', 1); define('PEAR_ERROR_PRINT', 2); define('PEAR_ERROR_TRIGGER', 4); @@ -31,6 +32,7 @@ define('PEAR_ERROR_CALLBACK', 16); * @deprecated */ define('PEAR_ERROR_EXCEPTION', 32); +/**#@-*/ define('PEAR_ZE2', (function_exists('version_compare') && version_compare(zend_version(), "2-dev", "ge"))); @@ -44,22 +46,13 @@ if (substr(PHP_OS, 0, 3) == 'WIN') { define('PEAR_OS', 'Unix'); // blatant assumption } -// instant backwards compatibility -if (!defined('PATH_SEPARATOR')) { - if (OS_WINDOWS) { - define('PATH_SEPARATOR', ';'); - } else { - define('PATH_SEPARATOR', ':'); - } -} - $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; $GLOBALS['_PEAR_destructor_object_list'] = array(); $GLOBALS['_PEAR_shutdown_funcs'] = array(); $GLOBALS['_PEAR_error_handler_stack'] = array(); -ini_set('track_errors', true); +@ini_set('track_errors', true); /** * Base class for other PEAR classes. Provides rudimentary @@ -78,9 +71,18 @@ ini_set('track_errors', true); * IMPORTANT! To use the emulated destructors you need to create the * objects by reference: $obj =& new PEAR_child; * - * @since PHP 4.0.2 - * @author Stig Bakken - * @see http://pear.php.net/manual/ + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Greg Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.0 + * @link http://pear.php.net/package/PEAR + * @see PEAR_Error + * @since Class available since PHP 4.0.2 + * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear */ class PEAR { @@ -164,10 +166,9 @@ class PEAR if (method_exists($this, $destructor)) { global $_PEAR_destructor_object_list; $_PEAR_destructor_object_list[] = &$this; - static $registered = false; - if (!$registered) { + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { register_shutdown_function("_PEAR_call_destructors"); - $registered = true; + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; } break; } else { @@ -211,9 +212,17 @@ class PEAR * @return mixed A reference to the variable. If not set it will be * auto initialised to NULL. */ - static function &getStaticProperty($class, $var) + function &getStaticProperty($class, $var) { static $properties; + if (!isset($properties[$class])) { + $properties[$class] = array(); + } + + if (!array_key_exists($var, $properties[$class])) { + $properties[$class][$var] = null; + } + return $properties[$class][$var]; } @@ -231,6 +240,12 @@ class PEAR */ function registerShutdownFunc($func, $args = array()) { + // if we are called statically, there is a potential + // that no shutdown func is registered. Bug #6445 + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); } @@ -248,18 +263,19 @@ class PEAR * @access public * @return bool true if parameter is an error */ - static function isError($data, $code = null) + function isError($data, $code = null) { - if ($data instanceof PEAR_Error) { - if (is_null($code)) { - return true; - } elseif (is_string($code)) { - return $data->getMessage() == $code; - } else { - return $data->getCode() == $code; - } + if (!is_a($data, 'PEAR_Error')) { + return false; + } + + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; } - return false; + + return $data->getCode() == $code; } // }}} @@ -427,7 +443,6 @@ class PEAR function delExpect($error_code) { $deleted = false; - if ((is_array($error_code) && (0 != count($error_code)))) { // $error_code is a non-empty array here; // we walk through it trying to unset all @@ -447,10 +462,10 @@ class PEAR } else { return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME } - } else { - // $error_code is empty - return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME } + + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME } // }}} @@ -493,7 +508,7 @@ class PEAR * @see PEAR::setErrorHandling * @since PHP 4.0.5 */ - function raiseError($message = null, + function &raiseError($message = null, $code = null, $mode = null, $options = null, @@ -517,6 +532,7 @@ class PEAR $mode = PEAR_ERROR_RETURN; } } + // No mode given, try global ones if ($mode === null) { // Class error handler @@ -537,11 +553,20 @@ class PEAR } else { $ec = 'PEAR_Error'; } + + if (intval(PHP_VERSION) < 5) { + // little non-eval hack to fix bug #12147 + include 'PEAR/FixPHP5PEARWarnings.php'; + return $a; + } + if ($skipmsg) { - return new $ec($code, $mode, $options, $userinfo); + $a = new $ec($code, $mode, $options, $userinfo); } else { - return new $ec($message, $code, $mode, $options, $userinfo); + $a = new $ec($message, $code, $mode, $options, $userinfo); } + + return $a; } // }}} @@ -554,15 +579,17 @@ class PEAR * @param string $message * */ - function throwError($message = null, + function &throwError($message = null, $code = null, $userinfo = null) { if (isset($this) && is_a($this, 'PEAR')) { - return $this->raiseError($message, $code, null, null, $userinfo); - } else { - return PEAR::raiseError($message, $code, null, null, $userinfo); + $a = &$this->raiseError($message, $code, null, null, $userinfo); + return $a; } + + $a = &PEAR::raiseError($message, $code, null, null, $userinfo); + return $a; } // }}} @@ -713,6 +740,7 @@ class PEAR if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { return false; } + if (OS_WINDOWS) { $suffix = '.dll'; } elseif (PHP_OS == 'HP-UX') { @@ -724,14 +752,20 @@ class PEAR } else { $suffix = '.so'; } + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); } + return true; } // }}} } +if (PEAR_ZE2) { + include_once 'PEAR5.php'; +} + // {{{ _PEAR_call_destructors() function _PEAR_call_destructors() @@ -741,9 +775,16 @@ function _PEAR_call_destructors() sizeof($_PEAR_destructor_object_list)) { reset($_PEAR_destructor_object_list); - if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) { + if (PEAR_ZE2) { + $destructLifoExists = PEAR5::getStaticProperty('PEAR', 'destructlifo'); + } else { + $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); + } + + if ($destructLifoExists) { $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); } + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { $classname = get_class($objref); while ($classname) { @@ -762,7 +803,7 @@ function _PEAR_call_destructors() } // Now call the shutdown functions - if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { + if (isset($GLOBALS['_PEAR_shutdown_funcs']) AND is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { call_user_func_array($value[0], $value[1]); } @@ -770,7 +811,23 @@ function _PEAR_call_destructors() } // }}} - +/** + * Standard PEAR error class for PHP 4 + * + * This class is supserseded by {@link PEAR_Exception} in PHP 5 + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Gregory Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: 1.9.0 + * @link http://pear.php.net/manual/en/core.pear.pear-error.php + * @see PEAR::raiseError(), PEAR::throwError() + * @since Class available since PHP 4.0.2 + */ class PEAR_Error { // {{{ properties @@ -816,11 +873,20 @@ class PEAR_Error $this->code = $code; $this->mode = $mode; $this->userinfo = $userinfo; - if (function_exists("debug_backtrace")) { - if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) { - $this->backtrace = debug_backtrace(); + + if (PEAR_ZE2) { + $skiptrace = PEAR5::getStaticProperty('PEAR_Error', 'skiptrace'); + } else { + $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); + } + + if (!$skiptrace) { + $this->backtrace = debug_backtrace(); + if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { + unset($this->backtrace[0]['object']); } } + if ($mode & PEAR_ERROR_CALLBACK) { $this->level = E_USER_NOTICE; $this->callback = $options; @@ -828,20 +894,25 @@ class PEAR_Error if ($options === null) { $options = E_USER_NOTICE; } + $this->level = $options; $this->callback = null; } + if ($this->mode & PEAR_ERROR_PRINT) { if (is_null($options) || is_int($options)) { $format = "%s"; } else { $format = $options; } + printf($format, $this->getMessage()); } + if ($this->mode & PEAR_ERROR_TRIGGER) { trigger_error($this->getMessage(), $this->level); } + if ($this->mode & PEAR_ERROR_DIE) { $msg = $this->getMessage(); if (is_null($options) || is_int($options)) { @@ -854,42 +925,17 @@ class PEAR_Error } die(sprintf($format, $msg)); } + if ($this->mode & PEAR_ERROR_CALLBACK) { if (is_callable($this->callback)) { call_user_func($this->callback, $this); } } - if ($this->mode & PEAR_ERROR_EXCEPTION) { - trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_ErrorStack for exceptions", E_USER_WARNING); - eval('$e = new Exception($this->message, $this->code);$e->PEAR_Error = $this;throw($e);'); - } - static $hasError=false; - if (!$hasError) - { - if ($this instanceof KTEntityNoObjects) return; - $hasError = true; - $config = KTConfig::getSingleton(); - $logPearError = $config->get('tweaks/logPearError', false); - if ($logPearError !== true) return; - - global $default; - $msg = $this->getMessage(); - $ignore = array( - _kt('No licenses have been installed'), - _kt('Session timed out'), - _kt('You need to login to access this page'), - _kt('You are coming from a different IP address than the session requires'), - _kt('Anonymous logins are no longer allowed by the system administrator. Please login.'), - _kt('Your account has been disabled. Please contact the system administrator for assistance.')); - - if (in_array($msg, $ignore)) return; - $trace = debug_backtrace(); - - $default->log->info('PEAR_ERROR: ' . $msg); - $default->log->info('Stack Trace: ' . print_r($trace, true)); + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);throw($e);'); } - } // }}} @@ -1003,6 +1049,9 @@ class PEAR_Error */ function getBacktrace($frame = null) { + if (defined('PEAR_IGNORE_BACKTRACE')) { + return null; + } if ($frame === null) { return $this->backtrace; } @@ -1023,6 +1072,12 @@ class PEAR_Error // }}} // {{{ toString() + function __toString() + { + return $this->getMessage(); + } + // }}} + // {{{ toString() /** * Make a string representation of this object. @@ -1080,4 +1135,3 @@ class PEAR_Error * c-basic-offset: 4 * End: */ -?> diff --git a/thirdparty/pear/PEAR5.php b/thirdparty/pear/PEAR5.php new file mode 100644 index 0000000..4286067 --- /dev/null +++ b/thirdparty/pear/PEAR5.php @@ -0,0 +1,33 @@ + | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: Compat.php,v 1.19 2005/05/10 12:05:36 aidan Exp $ /** @@ -27,7 +27,7 @@ * * @category PHP * @package PHP_Compat - * @version $Revision$ + * @version $Revision: 1.19 $ * @author Aidan Lister * @static */ diff --git a/thirdparty/pear/PHP/Compat/Components.php b/thirdparty/pear/PHP/Compat/Components.php old mode 100644 new mode 100755 index b641de2..264240a --- a/thirdparty/pear/PHP/Compat/Components.php +++ b/thirdparty/pear/PHP/Compat/Components.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: Components.php,v 1.10 2005/12/05 14:49:56 aidan Exp $ // Functions @@ -24,12 +24,14 @@ $components['function']['array_chunk'] = '4.2.0'; $components['function']['array_combine'] = '5.0.0'; $components['function']['array_diff_assoc'] = '4.3.0'; $components['function']['array_diff_key'] = '5.0.2'; +$components['function']['array_diff_uassoc'] = '5.0.0'; $components['function']['array_diff_ukey'] = '5.0.2'; $components['function']['array_intersect_assoc'] = '5.0.0'; $components['function']['array_intersect_key'] = '5.0.2'; $components['function']['array_intersect_uassoc'] = '5.0.0'; $components['function']['array_intersect_ukey'] = '5.0.2'; $components['function']['array_key_exists'] = '4.1.0'; +$components['function']['array_product'] = '5.1.0'; $components['function']['array_search'] = '4.0.5'; $components['function']['array_udiff'] = '5.0.0'; $components['function']['array_udiff_assoc'] = '5.0.0'; @@ -38,6 +40,8 @@ $components['function']['array_uintersect'] = '5.0.0'; $components['function']['array_uintersect_assoc'] = '5.0.0'; $components['function']['array_uintersect_uassoc'] = '5.0.0'; $components['function']['array_walk_recursive'] = '5.0.0'; +$components['function']['bcinvert'] = '5.2.0'; +$components['function']['bcpowmod'] = '5.0.0'; $components['function']['call_user_func_array'] = '4.0.4'; $components['function']['clone'] = '5.0.0'; $components['function']['constant'] = '4.0.4'; @@ -55,16 +59,22 @@ $components['function']['html_entity_decode'] = '4.3.0'; $components['function']['htmlspecialchars_decode'] = '5.1.0'; $components['function']['http_build_query'] = '5.0.0'; $components['function']['ibase_timefmt'] = '5.0.0'; +$components['function']['idate'] = '5.1.0'; $components['function']['image_type_to_mime_type'] = '4.3.0'; +$components['function']['inet_ntop'] = '5.1.0'; +$components['function']['inet_pton'] = '5.1.0'; $components['function']['ini_get_all'] = '4.2.0'; $components['function']['is_a'] = '4.2.0'; +$components['function']['is_scalar'] = '4.0.5'; $components['function']['md5_file'] = '4.2.0'; $components['function']['mhash'] = '4.0.0'; +$components['function']['mime_content_type'] = '4.3.0'; $components['function']['ob_clean'] = '4.2.0'; $components['function']['ob_flush'] = '4.2.0'; $components['function']['ob_get_clean'] = '4.3.0'; $components['function']['ob_get_flush'] = '4.3.0'; $components['function']['php_strip_whitespace'] = '5.0.0'; +$components['function']['property_exists'] = '5.1.0'; $components['function']['pg_affected_rows'] = '4.2.0'; $components['function']['pg_escape_bytea'] = '4.2.0'; $components['function']['pg_unescape_bytea'] = '4.2.0'; @@ -80,6 +90,7 @@ $components['function']['stripos'] = '5.0.0'; $components['function']['strpbrk'] = '5.0.0'; $components['function']['strripos'] = '5.0.0'; $components['function']['substr_compare'] = '5.0.0'; +$components['function']['time_sleep_until'] = '5.1.0'; $components['function']['var_export'] = '4.2.0'; $components['function']['version_compare'] = '4.1.0'; $components['function']['vprintf'] = '4.1.0'; @@ -89,7 +100,7 @@ $components['function']['vsprintf'] = '4.1.0'; $components['constant']['DIRECTORY_SEPARATOR'] = '4.0.6'; $components['constant']['E_STRICT'] = '5.0.0'; $components['constant']['FILE'] = '4.3.0'; -$components['constant']['PATH_SEPARATOR'] = '4.2.0'; +$components['constant']['PATH_SEPARATOR'] = '4.3.0'; $components['constant']['PHP_EOL'] = '5.0.1'; $components['constant']['STD'] = '4.3.0'; $components['constant']['T'] = '5.0.0'; diff --git a/thirdparty/pear/PHP/Compat/Constant/DIRECTORY_SEPARATOR.php b/thirdparty/pear/PHP/Compat/Constant/DIRECTORY_SEPARATOR.php old mode 100644 new mode 100755 index c28e56a..5c255b5 --- a/thirdparty/pear/PHP/Compat/Constant/DIRECTORY_SEPARATOR.php +++ b/thirdparty/pear/PHP/Compat/Constant/DIRECTORY_SEPARATOR.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: DIRECTORY_SEPARATOR.php,v 1.1 2004/11/28 03:55:28 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/reserved.constants.standard * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.1 $ * @since PHP 4.0.6 */ if (!defined('DIRECTORY_SEPARATOR')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/E_STRICT.php b/thirdparty/pear/PHP/Compat/Constant/E_STRICT.php old mode 100644 new mode 100755 index c57c480..19a162e --- a/thirdparty/pear/PHP/Compat/Constant/E_STRICT.php +++ b/thirdparty/pear/PHP/Compat/Constant/E_STRICT.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: E_STRICT.php,v 1.11 2004/08/19 10:09:52 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/ref.errorfunc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.11 $ * @since PHP 5 */ if (!defined('E_STRICT')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/FILE.php b/thirdparty/pear/PHP/Compat/Constant/FILE.php old mode 100644 new mode 100755 index ca0b886..3f2d9d4 --- a/thirdparty/pear/PHP/Compat/Constant/FILE.php +++ b/thirdparty/pear/PHP/Compat/Constant/FILE.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: FILE.php,v 1.8 2004/08/19 10:09:52 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/ref.filesystem * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.8 $ * @since PHP 5 */ if (!defined('FILE_USE_INCLUDE_PATH')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/PATH_SEPARATOR.php b/thirdparty/pear/PHP/Compat/Constant/PATH_SEPARATOR.php old mode 100644 new mode 100755 index a3fc4c5..db40ca7 --- a/thirdparty/pear/PHP/Compat/Constant/PATH_SEPARATOR.php +++ b/thirdparty/pear/PHP/Compat/Constant/PATH_SEPARATOR.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: PATH_SEPARATOR.php,v 1.13 2004/11/14 16:10:18 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/ref.dir * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.13 $ * @since PHP 4.3.0 */ if (!defined('PATH_SEPARATOR')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/PHP_EOL.php b/thirdparty/pear/PHP/Compat/Constant/PHP_EOL.php old mode 100644 new mode 100755 index ac124ae..dec28ea --- a/thirdparty/pear/PHP/Compat/Constant/PHP_EOL.php +++ b/thirdparty/pear/PHP/Compat/Constant/PHP_EOL.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: PHP_EOL.php,v 1.2 2004/10/10 13:58:09 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/reserved.constants.core * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.2 $ * @since PHP 5.0.2 */ if (!defined('PHP_EOL')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/STD.php b/thirdparty/pear/PHP/Compat/Constant/STD.php old mode 100644 new mode 100755 index 06adb39..56e47b0 --- a/thirdparty/pear/PHP/Compat/Constant/STD.php +++ b/thirdparty/pear/PHP/Compat/Constant/STD.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: STD.php,v 1.9 2004/08/19 10:09:52 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/features.commandline * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.9 $ * @since PHP 4.3.0 */ if (!defined('STDIN')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/T.php b/thirdparty/pear/PHP/Compat/Constant/T.php old mode 100644 new mode 100755 index 700a9a9..8e32402 --- a/thirdparty/pear/PHP/Compat/Constant/T.php +++ b/thirdparty/pear/PHP/Compat/Constant/T.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: T.php,v 1.4 2004/11/14 16:43:40 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/ref.tokenizer * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 5 */ if (!defined('T_ML_COMMENT')) { diff --git a/thirdparty/pear/PHP/Compat/Constant/UPLOAD_ERR.php b/thirdparty/pear/PHP/Compat/Constant/UPLOAD_ERR.php old mode 100644 new mode 100755 index 4d41b05..423aeb0 --- a/thirdparty/pear/PHP/Compat/Constant/UPLOAD_ERR.php +++ b/thirdparty/pear/PHP/Compat/Constant/UPLOAD_ERR.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: UPLOAD_ERR.php,v 1.1 2004/10/12 08:41:11 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/features.file-upload.errors * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.1 $ * @since PHP 4.3.0 */ if (!defined('UPLOAD_ERR_OK')) { diff --git a/thirdparty/pear/PHP/Compat/Function/array_change_key_case.php b/thirdparty/pear/PHP/Compat/Function/array_change_key_case.php old mode 100644 new mode 100755 index 0631759..f3fb30a --- a/thirdparty/pear/PHP/Compat/Function/array_change_key_case.php +++ b/thirdparty/pear/PHP/Compat/Function/array_change_key_case.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_change_key_case.php,v 1.11 2005/12/07 21:08:57 aidan Exp $ if (!defined('CASE_LOWER')) { @@ -36,7 +36,7 @@ if (!defined('CASE_UPPER')) { * @link http://php.net/function.array_change_key_case * @author Stephan Schmidt * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.11 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ @@ -60,4 +60,5 @@ if (!function_exists('array_change_key_case')) { return $output; } } + ?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/array_chunk.php b/thirdparty/pear/PHP/Compat/Function/array_chunk.php old mode 100644 new mode 100755 index 98a4415..76d2ba7 --- a/thirdparty/pear/PHP/Compat/Function/array_chunk.php +++ b/thirdparty/pear/PHP/Compat/Function/array_chunk.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_chunk.php,v 1.14 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.array_chunk * @author Aidan Lister * @author Thiemo Mttig (http://maettig.com) - * @version $Revision$ + * @version $Revision: 1.14 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_combine.php b/thirdparty/pear/PHP/Compat/Function/array_combine.php old mode 100644 new mode 100755 index ed9f8b0..90ddd60 --- a/thirdparty/pear/PHP/Compat/Function/array_combine.php +++ b/thirdparty/pear/PHP/Compat/Function/array_combine.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_combine.php,v 1.21 2005/01/28 02:27:52 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_combine * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.21 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_diff_assoc.php b/thirdparty/pear/PHP/Compat/Function/array_diff_assoc.php old mode 100644 new mode 100755 index be4917c..c129e5d --- a/thirdparty/pear/PHP/Compat/Function/array_diff_assoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_diff_assoc.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_diff_assoc.php,v 1.12 2005/12/07 21:08:57 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_diff_assoc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.12 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ @@ -72,4 +72,5 @@ if (!function_exists('array_diff_assoc')) { return $array_comp; } } + ?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/array_diff_key.php b/thirdparty/pear/PHP/Compat/Function/array_diff_key.php old mode 100644 new mode 100755 index 6572fe9..f9eff6d --- a/thirdparty/pear/PHP/Compat/Function/array_diff_key.php +++ b/thirdparty/pear/PHP/Compat/Function/array_diff_key.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_diff_key.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_diff_key * @author Tom Buskens - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 5.0.2 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_diff_uassoc.php b/thirdparty/pear/PHP/Compat/Function/array_diff_uassoc.php old mode 100644 new mode 100755 index da15020..b896edf --- a/thirdparty/pear/PHP/Compat/Function/array_diff_uassoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_diff_uassoc.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_diff_uassoc.php,v 1.2 2005/01/26 04:55:13 aidan Exp $ /** @@ -24,7 +24,7 @@ * @category PHP * @package PHP_Compat * @link http://php.net/function.array_diff_uassoc - * @version $Revision$ + * @version $Revision: 1.2 $ * @since PHP 5.0.0 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_diff_ukey.php b/thirdparty/pear/PHP/Compat/Function/array_diff_ukey.php old mode 100644 new mode 100755 index 95b9711..9fa7e3a --- a/thirdparty/pear/PHP/Compat/Function/array_diff_ukey.php +++ b/thirdparty/pear/PHP/Compat/Function/array_diff_ukey.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_diff_ukey.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_diff_ukey * @author Tom Buskens - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 5.0.2 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_intersect_assoc.php b/thirdparty/pear/PHP/Compat/Function/array_intersect_assoc.php old mode 100644 new mode 100755 index 72ebf4e..515fe0b --- a/thirdparty/pear/PHP/Compat/Function/array_intersect_assoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_intersect_assoc.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_intersect_assoc.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_intersect_assoc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_intersect_key.php b/thirdparty/pear/PHP/Compat/Function/array_intersect_key.php old mode 100644 new mode 100755 index 0cd5099..4c78da1 --- a/thirdparty/pear/PHP/Compat/Function/array_intersect_key.php +++ b/thirdparty/pear/PHP/Compat/Function/array_intersect_key.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_intersect_key.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_intersect_key * @author Tom Buskens - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 5.0.2 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_intersect_uassoc.php b/thirdparty/pear/PHP/Compat/Function/array_intersect_uassoc.php old mode 100644 new mode 100755 index 7cc79bf..52da711 --- a/thirdparty/pear/PHP/Compat/Function/array_intersect_uassoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_intersect_uassoc.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_intersect_uassoc.php,v 1.5 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_intersect_uassoc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.5 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_intersect_ukey.php b/thirdparty/pear/PHP/Compat/Function/array_intersect_ukey.php old mode 100644 new mode 100755 index 83f9604..e27aea6 --- a/thirdparty/pear/PHP/Compat/Function/array_intersect_ukey.php +++ b/thirdparty/pear/PHP/Compat/Function/array_intersect_ukey.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_intersect_ukey.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_intersect_ukey * @author Tom Buskens - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 5.0.2 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_key_exists.php b/thirdparty/pear/PHP/Compat/Function/array_key_exists.php old mode 100644 new mode 100755 index 51467ca..b4cbab4 --- a/thirdparty/pear/PHP/Compat/Function/array_key_exists.php +++ b/thirdparty/pear/PHP/Compat/Function/array_key_exists.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_key_exists.php,v 1.7 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_key_exists * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.7 $ * @since PHP 4.1.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_product.php b/thirdparty/pear/PHP/Compat/Function/array_product.php new file mode 100644 index 0000000..206c8d9 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/array_product.php @@ -0,0 +1,53 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: array_product.php,v 1.1 2005/12/05 14:49:08 aidan Exp $ + + +/** + * Replace array_product() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/time_sleep_until + * @author Arpad Ray + * @version $Revision: 1.1 $ + * @since PHP 5.1.0 + * @require PHP 4.0.1 (trigger_error) + */ +if (!function_exists('array_product')) { + function array_product($array) + { + if (!is_array($array)) { + trigger_error('The argument should be an array', E_USER_WARNING); + return; + } + + if (empty($array)) { + return 0; + } + + $r = 1; + foreach ($array as $v) { + $r *= $v; + } + + return $r; + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/array_search.php b/thirdparty/pear/PHP/Compat/Function/array_search.php old mode 100644 new mode 100755 index c736bb3..e637b8b --- a/thirdparty/pear/PHP/Compat/Function/array_search.php +++ b/thirdparty/pear/PHP/Compat/Function/array_search.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_search.php,v 1.6 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.array_search * @author Aidan Lister * @author Thiemo Mttig (http://maettig.com/) - * @version $Revision$ + * @version $Revision: 1.6 $ * @since PHP 4.0.5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_udiff.php b/thirdparty/pear/PHP/Compat/Function/array_udiff.php old mode 100644 new mode 100755 index 7116353..08f2226 --- a/thirdparty/pear/PHP/Compat/Function/array_udiff.php +++ b/thirdparty/pear/PHP/Compat/Function/array_udiff.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_udiff.php,v 1.10 2005/01/26 04:55:13 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.array_udiff * @author Stephan Schmidt * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.10 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_udiff_assoc.php b/thirdparty/pear/PHP/Compat/Function/array_udiff_assoc.php old mode 100644 new mode 100755 index e3d51a0..c82f056 --- a/thirdparty/pear/PHP/Compat/Function/array_udiff_assoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_udiff_assoc.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_udiff_assoc.php,v 1.14 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @package PHP_Compat * @author Stephan Schmidt * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.14 $ * @link http://php.net/function.array-udiff-assoc * @since PHP 5 * @require PHP 4.0.6 (is_callable) diff --git a/thirdparty/pear/PHP/Compat/Function/array_udiff_uassoc.php b/thirdparty/pear/PHP/Compat/Function/array_udiff_uassoc.php old mode 100644 new mode 100755 index a4eb3ea..053a07c --- a/thirdparty/pear/PHP/Compat/Function/array_udiff_uassoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_udiff_uassoc.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_udiff_uassoc.php,v 1.8 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_udiff_uassoc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.8 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_uintersect.php b/thirdparty/pear/PHP/Compat/Function/array_uintersect.php old mode 100644 new mode 100755 index c111c7b..d9d38ff --- a/thirdparty/pear/PHP/Compat/Function/array_uintersect.php +++ b/thirdparty/pear/PHP/Compat/Function/array_uintersect.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_uintersect.php,v 1.9 2005/05/10 12:05:48 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.array_uintersect * @author Tom Buskens * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.9 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_uintersect_assoc.php b/thirdparty/pear/PHP/Compat/Function/array_uintersect_assoc.php old mode 100644 new mode 100755 index 4400356..299466b --- a/thirdparty/pear/PHP/Compat/Function/array_uintersect_assoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_uintersect_assoc.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_uintersect_assoc.php,v 1.9 2005/05/10 07:50:08 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.array_uintersect_assoc * @author Tom Buskens * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.9 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_uintersect_uassoc.php b/thirdparty/pear/PHP/Compat/Function/array_uintersect_uassoc.php old mode 100644 new mode 100755 index f3dbc05..369d0da --- a/thirdparty/pear/PHP/Compat/Function/array_uintersect_uassoc.php +++ b/thirdparty/pear/PHP/Compat/Function/array_uintersect_uassoc.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_uintersect_uassoc.php,v 1.12 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.array_uintersect_uassoc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.12 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/array_walk_recursive.php b/thirdparty/pear/PHP/Compat/Function/array_walk_recursive.php old mode 100644 new mode 100755 index ac515f6..0bebf99 --- a/thirdparty/pear/PHP/Compat/Function/array_walk_recursive.php +++ b/thirdparty/pear/PHP/Compat/Function/array_walk_recursive.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: array_walk_recursive.php,v 1.7 2005/01/26 04:55:13 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.array_walk_recursive * @author Tom Buskens * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.7 $ * @since PHP 5 * @require PHP 4.0.6 (is_callable) */ diff --git a/thirdparty/pear/PHP/Compat/Function/bcinvert.php b/thirdparty/pear/PHP/Compat/Function/bcinvert.php new file mode 100644 index 0000000..f876f57 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/bcinvert.php @@ -0,0 +1,76 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: bcinvert.php,v 1.2 2005/11/22 20:24:45 aidan Exp $ + + +/** + * Replace bcinvert() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/function.bcinvert + * @author Sara Golemon + * @version $Revision: 1.2 $ + * @since PHP 5.2.0 + * @require PHP 4.0.4 (call_user_func_array) + */ +if (!function_exists('bcinvert')) { + function bcinvert($a, $n) + { + // Sanity check + if (!is_scalar($a)) { + user_error('bcinvert() expects parameter 1 to be string, ' . + gettype($a) . ' given', E_USER_WARNING); + return false; + } + + if (!is_scalar($n)) { + user_error('bcinvert() expects parameter 2 to be string, ' . + gettype($n) . ' given', E_USER_WARNING); + return false; + } + + $u1 = $v2 = '1'; + $u2 = $v1 = '0'; + $u3 = $n; + $v3 = $a; + + while (bccomp($v3, '0')) { + $q0 = bcdiv($u3, $v3); + $t1 = bcsub($u1, bcmul($q0, $v1)); + $t2 = bcsub($u2, bcmul($q0, $v2)); + $t3 = bcsub($u3, bcmul($q0, $v3)); + + $u1 = $v1; + $u2 = $v2; + $u3 = $v3; + + $v1 = $t1; + $v2 = $t2; + $v3 = $t3; + } + + if (bccomp($u2, '0') < 0) { + return bcadd($u2, $n); + } else { + return bcmod($u2, $n); + } + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/bcpowmod.php b/thirdparty/pear/PHP/Compat/Function/bcpowmod.php new file mode 100644 index 0000000..f0cfd84 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/bcpowmod.php @@ -0,0 +1,75 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: bcpowmod.php,v 1.2 2005/11/22 20:24:45 aidan Exp $ + + +/** + * Replace bcpowmod() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/function.bcpowmod + * @author Sara Golemon + * @version $Revision: 1.2 $ + * @since PHP 5.0.0 + * @require PHP 4.0.0 (user_error) + */ +if (!function_exists('bcpowmod')) { + function bcpowmod($x, $y, $modulus, $scale) + { + // Sanity check + if (!is_scalar($x)) { + user_error('bcpowmod() expects parameter 1 to be string, ' . + gettype($x) . ' given', E_USER_WARNING); + return false; + } + + if (!is_scalar($y)) { + user_error('bcpowmod() expects parameter 2 to be string, ' . + gettype($y) . ' given', E_USER_WARNING); + return false; + } + + if (!is_scalar($modulus)) { + user_error('bcpowmod() expects parameter 3 to be string, ' . + gettype($modulus) . ' given', E_USER_WARNING); + return false; + } + + if (!is_scalar($scale)) { + user_error('bcpowmod() expects parameter 4 to be integer, ' . + gettype($scale) . ' given', E_USER_WARNING); + return false; + } + + $t = '1'; + while (bccomp($y, '0')) { + if (bccomp(bcmod($y, '2'), '0')) { + $t = bcmod(bcmul($t, $x), $modulus); + $y = bcsub($y, '1'); + } + + $x = bcmod(bcmul($x, $x), $modulus); + $y = bcdiv($y, '2'); + } + + return $t; + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/call_user_func_array.php b/thirdparty/pear/PHP/Compat/Function/call_user_func_array.php old mode 100644 new mode 100755 index 95b6785..924931c --- a/thirdparty/pear/PHP/Compat/Function/call_user_func_array.php +++ b/thirdparty/pear/PHP/Compat/Function/call_user_func_array.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: call_user_func_array.php,v 1.13 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.call_user_func_array * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.13 $ * @since PHP 4.0.4 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/clone.php b/thirdparty/pear/PHP/Compat/Function/clone.php old mode 100644 new mode 100755 index d90894e..33c4609 --- a/thirdparty/pear/PHP/Compat/Function/clone.php +++ b/thirdparty/pear/PHP/Compat/Function/clone.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: clone.php,v 1.3 2005/05/01 10:40:59 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/language.oop5.cloning * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.3 $ * @since PHP 5.0.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/constant.php b/thirdparty/pear/PHP/Compat/Function/constant.php old mode 100644 new mode 100755 index ca819f6..54d95e5 --- a/thirdparty/pear/PHP/Compat/Function/constant.php +++ b/thirdparty/pear/PHP/Compat/Function/constant.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: constant.php,v 1.7 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.constant * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.7 $ * @since PHP 4.0.4 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/convert_uudecode.php b/thirdparty/pear/PHP/Compat/Function/convert_uudecode.php old mode 100644 new mode 100755 index 99c8178..fb4180f --- a/thirdparty/pear/PHP/Compat/Function/convert_uudecode.php +++ b/thirdparty/pear/PHP/Compat/Function/convert_uudecode.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: convert_uudecode.php,v 1.8 2005/01/26 04:55:13 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.convert_uudecode * @author Michael Wallner * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.8 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/convert_uuencode.php b/thirdparty/pear/PHP/Compat/Function/convert_uuencode.php old mode 100644 new mode 100755 index 15b2451..5b4850c --- a/thirdparty/pear/PHP/Compat/Function/convert_uuencode.php +++ b/thirdparty/pear/PHP/Compat/Function/convert_uuencode.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: convert_uuencode.php,v 1.7 2005/01/26 04:55:13 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.convert_uuencode * @author Michael Wallner * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.7 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/debug_print_backtrace.php b/thirdparty/pear/PHP/Compat/Function/debug_print_backtrace.php old mode 100644 new mode 100755 index b4eba85..cc02d3b --- a/thirdparty/pear/PHP/Compat/Function/debug_print_backtrace.php +++ b/thirdparty/pear/PHP/Compat/Function/debug_print_backtrace.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: debug_print_backtrace.php,v 1.3 2005/08/17 02:58:09 aidan Exp $ /** @@ -27,9 +27,9 @@ * @link http://php.net/function.debug_print_backtrace * @author Laurent Laville * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.3 $ * @since PHP 5 - * @require PHP 4.0.0 + * @require PHP 4.3.0 (debug_backtrace) */ if (!function_exists('debug_print_backtrace')) { function debug_print_backtrace() diff --git a/thirdparty/pear/PHP/Compat/Function/file_get_contents.php b/thirdparty/pear/PHP/Compat/Function/file_get_contents.php old mode 100644 new mode 100755 index 2e9f1ca..2ef003d --- a/thirdparty/pear/PHP/Compat/Function/file_get_contents.php +++ b/thirdparty/pear/PHP/Compat/Function/file_get_contents.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: file_get_contents.php,v 1.21 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.file_get_contents * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.21 $ * @internal resource_context is not supported * @since PHP 5 * @require PHP 4.0.0 (user_error) diff --git a/thirdparty/pear/PHP/Compat/Function/file_put_contents.php b/thirdparty/pear/PHP/Compat/Function/file_put_contents.php old mode 100644 new mode 100755 index 1fe5b39..19daa07 --- a/thirdparty/pear/PHP/Compat/Function/file_put_contents.php +++ b/thirdparty/pear/PHP/Compat/Function/file_put_contents.php @@ -15,13 +15,17 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: file_put_contents.php,v 1.25 2005/12/05 09:25:15 aidan Exp $ if (!defined('FILE_USE_INCLUDE_PATH')) { define('FILE_USE_INCLUDE_PATH', 1); } +if (!defined('LOCK_EX')) { + define('LOCK_EX', 2); +} + if (!defined('FILE_APPEND')) { define('FILE_APPEND', 8); } @@ -34,7 +38,7 @@ if (!defined('FILE_APPEND')) { * @package PHP_Compat * @link http://php.net/function.file_put_contents * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.25 $ * @internal resource_context is not supported * @since PHP 5 * @require PHP 4.0.0 (user_error) @@ -60,7 +64,7 @@ if (!function_exists('file_put_contents')) { // Check what mode we are using $mode = ($flags & FILE_APPEND) ? 'a' : - 'w'; + 'wb'; // Check if we're using the include path $use_inc_path = ($flags & FILE_USE_INCLUDE_PATH) ? @@ -74,6 +78,14 @@ if (!function_exists('file_put_contents')) { return false; } + // Attempt to get an exclusive lock + $use_lock = ($flags & LOCK_EX) ? true : false ; + if ($use_lock === true) { + if (!flock($fh, LOCK_EX)) { + return false; + } + } + // Write to the file $bytes = 0; if (($bytes = @fwrite($fh, $content)) === false) { diff --git a/thirdparty/pear/PHP/Compat/Function/floatval.php b/thirdparty/pear/PHP/Compat/Function/floatval.php old mode 100644 new mode 100755 index d88c62d..63bab1a --- a/thirdparty/pear/PHP/Compat/Function/floatval.php +++ b/thirdparty/pear/PHP/Compat/Function/floatval.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: floatval.php,v 1.2 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.floatval * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.2 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) (Type Casting) */ diff --git a/thirdparty/pear/PHP/Compat/Function/fprintf.php b/thirdparty/pear/PHP/Compat/Function/fprintf.php old mode 100644 new mode 100755 index 6c50356..9a04a74 --- a/thirdparty/pear/PHP/Compat/Function/fprintf.php +++ b/thirdparty/pear/PHP/Compat/Function/fprintf.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: fprintf.php,v 1.13 2005/05/28 17:25:25 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.fprintf * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.13 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/fputcsv.php b/thirdparty/pear/PHP/Compat/Function/fputcsv.php old mode 100644 new mode 100755 index 8691bc3..86c7f9e --- a/thirdparty/pear/PHP/Compat/Function/fputcsv.php +++ b/thirdparty/pear/PHP/Compat/Function/fputcsv.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: fputcsv.php,v 1.2 2005/11/22 08:28:16 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.fprintf * @author Twebb * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.2 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ @@ -34,16 +34,16 @@ if (!function_exists('fputcsv')) { function fputcsv($handle, $fields, $delimiter = ',', $enclosure = '"') { // Sanity Check - if (!is_resource($extension)) { + if (!is_resource($handle)) { user_error('fputcsv() expects parameter 1 to be resource, ' . - gettype($extension) . ' given', E_USER_WARNING); + gettype($handle) . ' given', E_USER_WARNING); return false; } $str = ''; foreach ($fields as $cell) { - $cell = str_replace($enclosure, $enclosure.$enclosure, $cell); + $cell = str_replace($enclosure, $enclosure . $enclosure, $cell); if (strchr($cell, $delimiter) !== false || strchr($cell, $enclosure) !== false || diff --git a/thirdparty/pear/PHP/Compat/Function/get_headers.php b/thirdparty/pear/PHP/Compat/Function/get_headers.php old mode 100644 new mode 100755 index 5d66b89..ae3bf55 --- a/thirdparty/pear/PHP/Compat/Function/get_headers.php +++ b/thirdparty/pear/PHP/Compat/Function/get_headers.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: get_headers.php,v 1.1 2005/05/10 07:50:53 aidan Exp $ /** @@ -27,7 +27,7 @@ * @author Aeontech * @author Cpurruc * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.1 $ * @since PHP 5.0.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/get_include_path.php b/thirdparty/pear/PHP/Compat/Function/get_include_path.php old mode 100644 new mode 100755 index c014854..895a636 --- a/thirdparty/pear/PHP/Compat/Function/get_include_path.php +++ b/thirdparty/pear/PHP/Compat/Function/get_include_path.php @@ -15,7 +15,7 @@ // | Authors: Stephan Schmidt | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: get_include_path.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.get_include_path * @author Stephan Schmidt - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 4.3.0 * @require PHP 4.0.0 */ diff --git a/thirdparty/pear/PHP/Compat/Function/html_entity_decode.php b/thirdparty/pear/PHP/Compat/Function/html_entity_decode.php old mode 100644 new mode 100755 index d50971c..2da57ba --- a/thirdparty/pear/PHP/Compat/Function/html_entity_decode.php +++ b/thirdparty/pear/PHP/Compat/Function/html_entity_decode.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: html_entity_decode.php,v 1.8 2005/12/07 21:08:57 aidan Exp $ if (!defined('ENT_NOQUOTES')) { @@ -40,7 +40,7 @@ if (!defined('ENT_QUOTES')) { * @link http://php.net/function.html_entity_decode * @author David Irvine * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.8 $ * @since PHP 4.3.0 * @internal Setting the charset will not do anything * @require PHP 4.0.0 (user_error) @@ -69,4 +69,5 @@ if (!function_exists('html_entity_decode')) { return strtr($string, $trans_tbl); } } + ?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/htmlspecialchars_decode.php b/thirdparty/pear/PHP/Compat/Function/htmlspecialchars_decode.php old mode 100644 new mode 100755 index 6772121..73e779f --- a/thirdparty/pear/PHP/Compat/Function/htmlspecialchars_decode.php +++ b/thirdparty/pear/PHP/Compat/Function/htmlspecialchars_decode.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: htmlspecialchars_decode.php,v 1.3 2005/06/18 14:02:09 aidan Exp $ /** @@ -26,7 +26,7 @@ * @package PHP_Compat * @link http://php.net/function.htmlspecialchars_decode * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.3 $ * @since PHP 5.1.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/http_build_query.php b/thirdparty/pear/PHP/Compat/Function/http_build_query.php old mode 100644 new mode 100755 index 0ec4dda..169d2d1 --- a/thirdparty/pear/PHP/Compat/Function/http_build_query.php +++ b/thirdparty/pear/PHP/Compat/Function/http_build_query.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: http_build_query.php,v 1.16 2005/05/31 08:54:57 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.http-build-query * @author Stephan Schmidt * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.16 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/ibase_timefmt.php b/thirdparty/pear/PHP/Compat/Function/ibase_timefmt.php old mode 100644 new mode 100755 index b30cb59..4728ec5 --- a/thirdparty/pear/PHP/Compat/Function/ibase_timefmt.php +++ b/thirdparty/pear/PHP/Compat/Function/ibase_timefmt.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: ibase_timefmt.php,v 1.1 2005/05/10 07:51:07 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.ibase_timefmt * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.1 $ * @since PHP 5.0.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/idate.php b/thirdparty/pear/PHP/Compat/Function/idate.php new file mode 100644 index 0000000..847f27e --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/idate.php @@ -0,0 +1,52 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: idate.php,v 1.2 2005/12/07 21:08:57 aidan Exp $ + + +/** + * Replace idate() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/idate + * @author Arpad Ray + * @version $Revision: 1.2 $ + * @since PHP 5.0.0 + * @require PHP 4.0.0 (user_error) + */ +if (!function_exists('idate')) { + function idate($format, $timestamp = false) + { + if (strlen($format) !== 1) { + user_error('idate format is one char', E_USER_WARNING); + return false; + } + + if (strpos('BdhHiILmstUwWyYzZ', $format) === false) { + return 0; + } + + if ($timestamp === false) { + $timestamp = time(); + } + + return intval(date($format, $timestamp)); + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/image_type_to_mime_type.php b/thirdparty/pear/PHP/Compat/Function/image_type_to_mime_type.php old mode 100644 new mode 100755 index 5935a8b..38197a4 --- a/thirdparty/pear/PHP/Compat/Function/image_type_to_mime_type.php +++ b/thirdparty/pear/PHP/Compat/Function/image_type_to_mime_type.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: image_type_to_mime_type.php,v 1.8 2005/01/26 04:55:13 aidan Exp $ if (!defined('IMAGETYPE_GIF')) { @@ -90,7 +90,7 @@ if (!defined('IMAGETYPE_XBM')) { * @package PHP_Compat * @link http://php.net/function.image_type_to_mime_type * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.8 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/inet_ntop.php b/thirdparty/pear/PHP/Compat/Function/inet_ntop.php new file mode 100644 index 0000000..56c9331 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/inet_ntop.php @@ -0,0 +1,53 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: inet_ntop.php,v 1.3 2005/12/05 14:49:40 aidan Exp $ + + +/** + * Replace inet_ntop() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/inet_ntop + * @author Arpad Ray + * @version $Revision: 1.3 $ + * @since PHP 5.1.0 + * @require PHP 4.0.0 (long2ip) + */ +if (!function_exists('inet_ntop')) { + function inet_ntop($in_addr) + { + switch (strlen($in_addr)) { + case 4: + list(,$r) = unpack('N', $in_addr); + return long2ip($r); + + case 16: + $r = substr(chunk_split(bin2hex($in_addr), 4, ':'), 0, -1); + $r = preg_replace( + array('/(?::?\b0+\b:?){2,}/', '/\b0+([^0])/e'), + array('::', '(int)"$1"?"$1":"0$1"'), + $r); + return $r; + } + + return false; + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/inet_pton.php b/thirdparty/pear/PHP/Compat/Function/inet_pton.php new file mode 100644 index 0000000..97119a1 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/inet_pton.php @@ -0,0 +1,60 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: inet_pton.php,v 1.2 2005/12/05 14:49:40 aidan Exp $ + + +/** + * Replace inet_pton() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/inet_pton + * @author Arpad Ray + * @version $Revision: 1.2 $ + * @since PHP 5.1.0 + * @require PHP 4.2.0 (array_fill) + */ +if (!function_exists('inet_pton')) { + function inet_pton($address) + { + $r = ip2long($address); + if ($r !== false && $r != -1) { + return pack('N', $r); + } + + $delim_count = substr_count($address, ':'); + if ($delim_count < 1 || $delim_count > 7) { + return false; + } + + $r = explode(':', $address); + $rcount = count($r); + if (($doub = array_search('', $r, 1)) !== false) { + $length = (!$doub || $doub == $rcount - 1 ? 2 : 1); + array_splice($r, $doub, $length, array_fill(0, 8 + $length - $rcount, 0)); + } + + $r = array_map('hexdec', $r); + array_unshift($r, 'n*'); + $r = call_user_func_array('pack', $r); + + return $r; + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/ini_get_all.php b/thirdparty/pear/PHP/Compat/Function/ini_get_all.php old mode 100644 new mode 100755 index 1ba6ef7..5a57c73 --- a/thirdparty/pear/PHP/Compat/Function/ini_get_all.php +++ b/thirdparty/pear/PHP/Compat/Function/ini_get_all.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: ini_get_all.php,v 1.3 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.ini_get_all * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.3 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/is_a.php b/thirdparty/pear/PHP/Compat/Function/is_a.php old mode 100644 new mode 100755 index 1440e04..b5168f4 --- a/thirdparty/pear/PHP/Compat/Function/is_a.php +++ b/thirdparty/pear/PHP/Compat/Function/is_a.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: is_a.php,v 1.16 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.is_a * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.16 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) (is_subclass_of) */ diff --git a/thirdparty/pear/PHP/Compat/Function/md5_file.php b/thirdparty/pear/PHP/Compat/Function/md5_file.php old mode 100644 new mode 100755 index c970dd2..364d886 --- a/thirdparty/pear/PHP/Compat/Function/md5_file.php +++ b/thirdparty/pear/PHP/Compat/Function/md5_file.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: md5_file.php,v 1.3 2005/11/22 08:29:19 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/md5_file * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.3 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ @@ -70,6 +70,7 @@ if (!function_exists('md5_file')) { fclose($fh); // Return + $data = md5($data); if ($raw_output === true) { $data = pack('H*', $data); } diff --git a/thirdparty/pear/PHP/Compat/Function/mhash.php b/thirdparty/pear/PHP/Compat/Function/mhash.php old mode 100644 new mode 100755 index c2e41a0..0376f84 --- a/thirdparty/pear/PHP/Compat/Function/mhash.php +++ b/thirdparty/pear/PHP/Compat/Function/mhash.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: mhash.php,v 1.1 2005/05/10 07:56:44 aidan Exp $ if (!defined('MHASH_CRC32')) { @@ -90,7 +90,7 @@ if (!defined('MHASH_ADLER32')) { * @package PHP_Compat * @link http://php.net/function.mhash * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.1 $ * @since PHP 4.1.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/mime_content_type.php b/thirdparty/pear/PHP/Compat/Function/mime_content_type.php new file mode 100644 index 0000000..5924cb1 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/mime_content_type.php @@ -0,0 +1,63 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: mime_content_type.php,v 1.3 2005/12/07 21:08:57 aidan Exp $ + + +/** +* Replace mime_content_type() +* +* You will need the `file` command installed and present in your $PATH. If +* `file` is not available, the type 'application/octet-stream' is returned +* for all files. +* +* @category PHP +* @package PHP_Compat +* @link http://php.net/function.mime_content_type +* @version $Revision: 1.3 $ +* @author Ian Eure +* @since PHP 4.3.0 +* @require PHP 4.0.3 (escapeshellarg) +*/ +if (!function_exists('mime_content_type')) { + function mime_content_type($filename) + { + // Sanity check + if (!file_exists($filename)) { + return false; + } + + $filename = escapeshellarg($filename); + $out = `file -iL $filename 2>/dev/null`; + if (empty($out)) { + return 'application/octet-stream'; + } + + // Strip off filename + $t = substr($out, strpos($out, ':') + 2); + + if (strpos($t, ';') !== false) { + // Strip MIME parameters + $t = substr($t, 0, strpos($t, ';')); + } + + // Strip any remaining whitespace + return trim($t); + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/ob_clean.php b/thirdparty/pear/PHP/Compat/Function/ob_clean.php old mode 100644 new mode 100755 index cf8e38b..c5cdc19 --- a/thirdparty/pear/PHP/Compat/Function/ob_clean.php +++ b/thirdparty/pear/PHP/Compat/Function/ob_clean.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: ob_clean.php,v 1.6 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.ob_clean * @author Aidan Lister * @author Thiemo Mttig (http://maettig.com/) - * @version $Revision$ + * @version $Revision: 1.6 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/ob_flush.php b/thirdparty/pear/PHP/Compat/Function/ob_flush.php old mode 100644 new mode 100755 index 67bf96a..7ccdd69 --- a/thirdparty/pear/PHP/Compat/Function/ob_flush.php +++ b/thirdparty/pear/PHP/Compat/Function/ob_flush.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: ob_flush.php,v 1.6 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.ob_flush * @author Aidan Lister * @author Thiemo Mttig (http://maettig.com/) - * @version $Revision$ + * @version $Revision: 1.6 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/ob_get_clean.php b/thirdparty/pear/PHP/Compat/Function/ob_get_clean.php old mode 100644 new mode 100755 index 3b5f37f..4d9ccda --- a/thirdparty/pear/PHP/Compat/Function/ob_get_clean.php +++ b/thirdparty/pear/PHP/Compat/Function/ob_get_clean.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: ob_get_clean.php,v 1.6 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.ob_get_clean * @author Aidan Lister * @author Thiemo Mttig (http://maettig.com/) - * @version $Revision$ + * @version $Revision: 1.6 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/ob_get_flush.php b/thirdparty/pear/PHP/Compat/Function/ob_get_flush.php old mode 100644 new mode 100755 index 32b7068..86c7242 --- a/thirdparty/pear/PHP/Compat/Function/ob_get_flush.php +++ b/thirdparty/pear/PHP/Compat/Function/ob_get_flush.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: ob_get_flush.php,v 1.6 2005/01/26 04:55:13 aidan Exp $ /** @@ -26,7 +26,7 @@ * @link http://php.net/function.ob_get_flush * @author Aidan Lister * @author Thiemo Mttig (http://maettig.com/) - * @version $Revision$ + * @version $Revision: 1.6 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/pg_affected_rows.php b/thirdparty/pear/PHP/Compat/Function/pg_affected_rows.php old mode 100644 new mode 100755 index ecdd08d..8796141 --- a/thirdparty/pear/PHP/Compat/Function/pg_affected_rows.php +++ b/thirdparty/pear/PHP/Compat/Function/pg_affected_rows.php @@ -16,7 +16,7 @@ // | Mocha (http://us4.php.net/pg_escape_bytea) | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: pg_affected_rows.php,v 1.1 2005/05/10 07:56:51 aidan Exp $ /** diff --git a/thirdparty/pear/PHP/Compat/Function/pg_escape_bytea.php b/thirdparty/pear/PHP/Compat/Function/pg_escape_bytea.php old mode 100644 new mode 100755 index de14b95..e1a4ff6 --- a/thirdparty/pear/PHP/Compat/Function/pg_escape_bytea.php +++ b/thirdparty/pear/PHP/Compat/Function/pg_escape_bytea.php @@ -16,7 +16,7 @@ // | Mocha (http://us4.php.net/pg_escape_bytea) | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: pg_escape_bytea.php,v 1.1 2005/05/10 07:56:51 aidan Exp $ /** diff --git a/thirdparty/pear/PHP/Compat/Function/pg_unescape_bytea.php b/thirdparty/pear/PHP/Compat/Function/pg_unescape_bytea.php old mode 100644 new mode 100755 index e93fcd3..7a08e0d --- a/thirdparty/pear/PHP/Compat/Function/pg_unescape_bytea.php +++ b/thirdparty/pear/PHP/Compat/Function/pg_unescape_bytea.php @@ -16,7 +16,7 @@ // | Tobias | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: pg_unescape_bytea.php,v 1.2 2005/12/07 21:08:57 aidan Exp $ /** @@ -39,4 +39,5 @@ if (!function_exists('pg_unescape_bytea')) { $data); } } + ?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/php_strip_whitespace.php b/thirdparty/pear/PHP/Compat/Function/php_strip_whitespace.php old mode 100644 new mode 100755 index 7246881..b3e64a8 --- a/thirdparty/pear/PHP/Compat/Function/php_strip_whitespace.php +++ b/thirdparty/pear/PHP/Compat/Function/php_strip_whitespace.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: php_strip_whitespace.php,v 1.10 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.php_strip_whitespace * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.10 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) + Tokenizer extension */ diff --git a/thirdparty/pear/PHP/Compat/Function/restore_include_path.php b/thirdparty/pear/PHP/Compat/Function/restore_include_path.php old mode 100644 new mode 100755 index 757f273..cca915e --- a/thirdparty/pear/PHP/Compat/Function/restore_include_path.php +++ b/thirdparty/pear/PHP/Compat/Function/restore_include_path.php @@ -15,7 +15,7 @@ // | Authors: Stephan Schmidt | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: restore_include_path.php,v 1.4 2005/12/07 21:08:57 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.restore_include_path * @author Stephan Schmidt - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 4.3.0 */ if (!function_exists('restore_include_path')) { @@ -34,4 +34,5 @@ if (!function_exists('restore_include_path')) { return ini_restore('include_path'); } } + ?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/scandir.php b/thirdparty/pear/PHP/Compat/Function/scandir.php old mode 100644 new mode 100755 index c6e713f..4732591 --- a/thirdparty/pear/PHP/Compat/Function/scandir.php +++ b/thirdparty/pear/PHP/Compat/Function/scandir.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: scandir.php,v 1.18 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.scandir * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.18 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/set_include_path.php b/thirdparty/pear/PHP/Compat/Function/set_include_path.php old mode 100644 new mode 100755 index a9bd372..a48f881 --- a/thirdparty/pear/PHP/Compat/Function/set_include_path.php +++ b/thirdparty/pear/PHP/Compat/Function/set_include_path.php @@ -15,7 +15,7 @@ // | Authors: Stephan Schmidt | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: set_include_path.php,v 1.4 2005/12/07 21:08:57 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.set_include_path * @author Stephan Schmidt - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 4.3.0 */ if (!function_exists('set_include_path')) { @@ -34,4 +34,5 @@ if (!function_exists('set_include_path')) { return ini_set('include_path', $new_include_path); } } + ?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/str_ireplace.php b/thirdparty/pear/PHP/Compat/Function/str_ireplace.php old mode 100644 new mode 100755 index e46bd7b..0190a24 --- a/thirdparty/pear/PHP/Compat/Function/str_ireplace.php +++ b/thirdparty/pear/PHP/Compat/Function/str_ireplace.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: str_ireplace.php,v 1.18 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.str_ireplace * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.18 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) * @note count not by returned by reference, to enable diff --git a/thirdparty/pear/PHP/Compat/Function/str_rot13.php b/thirdparty/pear/PHP/Compat/Function/str_rot13.php old mode 100644 new mode 100755 index 801cc8c..34fa2a3 --- a/thirdparty/pear/PHP/Compat/Function/str_rot13.php +++ b/thirdparty/pear/PHP/Compat/Function/str_rot13.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: str_rot13.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.str_rot13 * @author Alan Morey * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 4.0.0 */ if (!function_exists('str_rot13')) { diff --git a/thirdparty/pear/PHP/Compat/Function/str_shuffle.php b/thirdparty/pear/PHP/Compat/Function/str_shuffle.php old mode 100644 new mode 100755 index e8e5c74..7ae236c --- a/thirdparty/pear/PHP/Compat/Function/str_shuffle.php +++ b/thirdparty/pear/PHP/Compat/Function/str_shuffle.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: str_shuffle.php,v 1.6 2005/08/14 03:24:16 aidan Exp $ /** @@ -25,15 +25,14 @@ * @package PHP_Compat * @link http://php.net/function.str_shuffle * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.6 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ if (!function_exists('str_shuffle')) { function str_shuffle($str) { - $newstr = ''; - $strlen = strlen($str); + // Init $str = (string) $str; // Seed @@ -42,11 +41,11 @@ if (!function_exists('str_shuffle')) { mt_srand($seed); // Shuffle - for ($i = 0; $strlen > $i; $i++) { - $newstr .= $str[mt_rand(0, $strlen - 1)]; + for ($new = '', $len = strlen($str); $len > 0; $str{$p} = $str{$len}) { + $new .= $str{$p = mt_rand(0, --$len)}; } - return $newstr; + return $new; } } diff --git a/thirdparty/pear/PHP/Compat/Function/str_split.php b/thirdparty/pear/PHP/Compat/Function/str_split.php old mode 100644 new mode 100755 index 2b9d5dc..21005ef --- a/thirdparty/pear/PHP/Compat/Function/str_split.php +++ b/thirdparty/pear/PHP/Compat/Function/str_split.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: str_split.php,v 1.15 2005/06/18 12:15:32 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.str_split * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.15 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/str_word_count.php b/thirdparty/pear/PHP/Compat/Function/str_word_count.php old mode 100644 new mode 100755 index 66ab3b0..5b91975 --- a/thirdparty/pear/PHP/Compat/Function/str_word_count.php +++ b/thirdparty/pear/PHP/Compat/Function/str_word_count.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: str_word_count.php,v 1.9 2005/02/28 11:45:28 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.str_word_count * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.9 $ * @since PHP 4.3.0 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/stripos.php b/thirdparty/pear/PHP/Compat/Function/stripos.php old mode 100644 new mode 100755 index 67e4501..09767ee --- a/thirdparty/pear/PHP/Compat/Function/stripos.php +++ b/thirdparty/pear/PHP/Compat/Function/stripos.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: stripos.php,v 1.13 2005/05/30 20:33:03 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.stripos * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.13 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/strpbrk.php b/thirdparty/pear/PHP/Compat/Function/strpbrk.php old mode 100644 new mode 100755 index 342bab5..951ce31 --- a/thirdparty/pear/PHP/Compat/Function/strpbrk.php +++ b/thirdparty/pear/PHP/Compat/Function/strpbrk.php @@ -15,7 +15,7 @@ // | Authors: Stephan Schmidt | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: strpbrk.php,v 1.4 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.strpbrk * @author Stephan Schmidt - * @version $Revision$ + * @version $Revision: 1.4 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/strripos.php b/thirdparty/pear/PHP/Compat/Function/strripos.php old mode 100644 new mode 100755 index a46c61e..5df033c --- a/thirdparty/pear/PHP/Compat/Function/strripos.php +++ b/thirdparty/pear/PHP/Compat/Function/strripos.php @@ -16,7 +16,7 @@ // | Stephan Schmidt | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: strripos.php,v 1.24 2005/08/10 10:19:59 aidan Exp $ /** @@ -26,7 +26,7 @@ * @package PHP_Compat * @link http://php.net/function.strripos * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.24 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ @@ -65,7 +65,7 @@ if (!function_exists('strripos')) { $haystack_len + $offset + 1; // Reverse iterate haystack - while (--$p > $leftlimit) { + while (--$p >= $leftlimit) { if ($needle_fc === $haystack{$p} && substr($haystack, $p, $needle_len) === $needle) { return $p; diff --git a/thirdparty/pear/PHP/Compat/Function/substr_compare.php b/thirdparty/pear/PHP/Compat/Function/substr_compare.php old mode 100644 new mode 100755 index 37b3395..61da8a6 --- a/thirdparty/pear/PHP/Compat/Function/substr_compare.php +++ b/thirdparty/pear/PHP/Compat/Function/substr_compare.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: substr_compare.php,v 1.5 2005/01/26 04:55:13 aidan Exp $ /** @@ -27,7 +27,7 @@ * @link http://php.net/function.substr_compare * @author Tom Buskens * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.5 $ * @since PHP 5 * @require PHP 4.0.0 (user_error) */ diff --git a/thirdparty/pear/PHP/Compat/Function/time_sleep_until.php b/thirdparty/pear/PHP/Compat/Function/time_sleep_until.php new file mode 100644 index 0000000..28939e6 --- /dev/null +++ b/thirdparty/pear/PHP/Compat/Function/time_sleep_until.php @@ -0,0 +1,48 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: time_sleep_until.php,v 1.2 2005/12/07 21:08:57 aidan Exp $ + + +/** + * Replace time_sleep_until() + * + * @category PHP + * @package PHP_Compat + * @link http://php.net/time_sleep_until + * @author Arpad Ray + * @version $Revision: 1.2 $ + * @since PHP 5.1.0 + * @require PHP 4.0.1 (trigger_error) + */ +if (!function_exists('time_sleep_until')) { + function time_sleep_until($timestamp) + { + list($usec, $sec) = explode(' ', microtime()); + $now = $sec + $usec; + if ($timestamp <= $now) { + user_error('Specified timestamp is in the past', E_USER_WARNING); + return false; + } + + $diff = $timestamp - $now; + usleep($diff * 1000000); + return true; + } +} + +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/var_export.php b/thirdparty/pear/PHP/Compat/Function/var_export.php old mode 100644 new mode 100755 index a6836f1..18ae36b --- a/thirdparty/pear/PHP/Compat/Function/var_export.php +++ b/thirdparty/pear/PHP/Compat/Function/var_export.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: var_export.php,v 1.15 2005/12/05 14:24:27 aidan Exp $ /** @@ -25,14 +25,14 @@ * @package PHP_Compat * @link http://php.net/function.var_export * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.15 $ * @since PHP 4.2.0 * @require PHP 4.0.0 (user_error) */ if (!function_exists('var_export')) { - function var_export($array, $return = false) + function var_export($var, $return = false, $level = 0) { - // Common output variables + // Init $indent = ' '; $doublearrow = ' => '; $lineend = ",\n"; @@ -40,58 +40,95 @@ if (!function_exists('var_export')) { $newline = "\n"; $find = array(null, '\\', '\''); $replace = array('NULL', '\\\\', '\\\''); + $out = ''; + + // Indent + $level++; + for ($i = 1, $previndent = ''; $i < $level; $i++) { + $previndent .= $indent; + } - // Check the export isn't a simple string / int - if (is_string($array)) { - $out = $stringdelim . $array . $stringdelim; - } elseif (is_int($array)) { - $out = (string)$array; - } else { - // Begin the array export - // Start the string - $out = "array (\n"; + // Handle each type + switch (gettype($var)) { + // Array + case 'array': + $out = 'array (' . $newline; + foreach ($var as $key => $value) { + // Key + if (is_string($key)) { + // Make key safe + for ($i = 0, $c = count($find); $i < $c; $i++) { + $var = str_replace($find[$i], $replace[$i], $var); + } + $key = $stringdelim . $key . $stringdelim; + } + + // Value + if (is_array($value)) { + $export = var_export($value, true, $level); + $value = $newline . $previndent . $indent . $export; + } else { + $value = var_export($value, true, $level); + } - // Loop through each value in array - foreach ($array as $key => $value) { - // If the key is a string, delimit it - if (is_string($key)) { - $key = str_replace($find, $replace, $key); - $key = $stringdelim . $key . $stringdelim; + // Piece line together + $out .= $previndent . $indent . $key . $doublearrow . $value . $lineend; } - // Delimit value - if (is_array($value)) { - // We have an array, so do some recursion - // Do some basic recursion while increasing the indent - $recur_array = explode($newline, var_export($value, true)); - $temp_array = array(); - foreach ($recur_array as $recur_line) { - $temp_array[] = $indent . $recur_line; - } - $recur_array = implode($newline, $temp_array); - $value = $newline . $recur_array; - } elseif (is_null($value)) { - $value = 'NULL'; - } else { - $value = str_replace($find, $replace, $value); - $value = $stringdelim . $value . $stringdelim; + // End string + $out .= $previndent . ')'; + break; + + // String + case 'string': + // Make the string safe + for ($i = 0, $c = count($find); $i < $c; $i++) { + $var = str_replace($find[$i], $replace[$i], $var); } + $out = $stringdelim . $var . $stringdelim; + break; - // Piece together the line - $out .= $indent . $key . $doublearrow . $value . $lineend; - } + // Number + case 'integer': + case 'double': + $out = (string) $var; + break; + + // Boolean + case 'boolean': + $out = $var ? 'true' : 'false'; + break; - // End our string - $out .= ")"; - } + // NULLs + case 'NULL': + case 'resource': + $out = 'NULL'; + break; + + // Objects + case 'object': + // Start the object export + $out = $newline . $previndent . 'class ' . get_class($var) . ' {' . $newline; + // Export the object vars + foreach (get_object_vars($var) as $key => $val) { + $out .= $previndent . ' var $' . $key . ' = '; + if (is_array($val)) { + $export = var_export($val, true, $level); + $out .= $newline . $previndent . $indent . $export . ';' . $newline; + } else { + $out .= var_export($val, true, $level) . ';' . $newline; + } + } + $out .= $previndent . '}'; + break; + } - // Decide method of output + // Method of output if ($return === true) { return $out; } else { echo $out; - return; } } } diff --git a/thirdparty/pear/PHP/Compat/Function/version_compare.php b/thirdparty/pear/PHP/Compat/Function/version_compare.php old mode 100644 new mode 100755 index 9c44eaf..fe84ffd --- a/thirdparty/pear/PHP/Compat/Function/version_compare.php +++ b/thirdparty/pear/PHP/Compat/Function/version_compare.php @@ -16,7 +16,7 @@ // | Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: version_compare.php,v 1.13 2005/08/01 12:21:14 aidan Exp $ /** @@ -27,9 +27,9 @@ * @link http://php.net/function.version_compare * @author Philippe Jausions * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.13 $ * @since PHP 4.1.0 - * @require PHP 4.0.0 (user_error) + * @require PHP 4.0.5 (user_error) */ if (!function_exists('version_compare')) { function version_compare($version1, $version2, $operator = '<') @@ -95,6 +95,7 @@ if (!function_exists('version_compare')) { $compare = ($i1 < $i2) ? -1 : 1; break; } + // We use the position of '#' in the versions list // for numbers... (so take care of # in original string) if ($i1 == '#') { @@ -102,11 +103,13 @@ if (!function_exists('version_compare')) { } elseif (is_numeric($i1)) { $i1 = '#'; } + if ($i2 == '#') { $i2 = ''; } elseif (is_numeric($i2)) { $i2 = '#'; } + if (isset($versions[$i1]) && isset($versions[$i2])) { $compare = ($versions[$i1] < $versions[$i2]) ? -1 : 1; } elseif (isset($versions[$i1])) { @@ -176,4 +179,4 @@ if (!function_exists('version_compare')) { } } -?> +?> \ No newline at end of file diff --git a/thirdparty/pear/PHP/Compat/Function/vprintf.php b/thirdparty/pear/PHP/Compat/Function/vprintf.php old mode 100644 new mode 100755 index 01b0c00..2f0830a --- a/thirdparty/pear/PHP/Compat/Function/vprintf.php +++ b/thirdparty/pear/PHP/Compat/Function/vprintf.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: vprintf.php,v 1.14 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.vprintf * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.14 $ * @since PHP 4.1.0 * @require PHP 4.0.4 (call_user_func_array) */ diff --git a/thirdparty/pear/PHP/Compat/Function/vsprintf.php b/thirdparty/pear/PHP/Compat/Function/vsprintf.php old mode 100644 new mode 100755 index ca3d52f..1b6be03 --- a/thirdparty/pear/PHP/Compat/Function/vsprintf.php +++ b/thirdparty/pear/PHP/Compat/Function/vsprintf.php @@ -15,7 +15,7 @@ // | Authors: Aidan Lister | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: vsprintf.php,v 1.10 2005/01/26 04:55:13 aidan Exp $ /** @@ -25,7 +25,7 @@ * @package PHP_Compat * @link http://php.net/function.vsprintf * @author Aidan Lister - * @version $Revision$ + * @version $Revision: 1.10 $ * @since PHP 4.1.0 * @require PHP 4.0.4 (call_user_func_array) */ diff --git a/thirdparty/pear/SOAP/Base.php b/thirdparty/pear/SOAP/Base.php old mode 100644 new mode 100755 index bd508dc..9ffa3a6 --- a/thirdparty/pear/SOAP/Base.php +++ b/thirdparty/pear/SOAP/Base.php @@ -19,68 +19,15 @@ * @author Shane Caraveo Port to PEAR and more * @author Chuck Hagenbuch Maintenance * @author Jan Schneider Maintenance - * @copyright 2003-2005 The PHP Group + * @copyright 2003-2007 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ -/** - * SOAP_OBJECT_STRUCT makes PEAR::SOAP use objects for SOAP structures rather - * than arrays. This has been done to provide a closer match to php-soap. If - * the old behaviour is needed, set to false. The old behaviour is - * deprecated. - * - * @global bool $GLOBALS['SOAP_OBJECT_STRUCT'] - */ -$GLOBALS['SOAP_OBJECT_STRUCT'] = true; - -/** - * SOAP_RAW_CONVERT makes PEAR::SOAP attempt to determine what SOAP type a PHP - * string COULD be. This may result in slightly better interoperability when - * you are not using WSDL, and are being lazy and not using SOAP_Value to - * define types for your values. - * - * @global bool $GLOBALS['SOAP_RAW_CONVERT'] - */ -$GLOBALS['SOAP_RAW_CONVERT'] = false; +/** Define linebreak sequence for the Mail_Mime package. */ +define('MAIL_MIMEPART_CRLF', "\r\n"); require_once 'PEAR.php'; -require_once 'SOAP/Type/dateTime.php'; -require_once 'SOAP/Type/hexBinary.php'; - -// optional features -$GLOBALS['SOAP_options'] = array(); - -@include_once 'Mail/mimePart.php'; -@include_once 'Mail/mimeDecode.php'; -if (class_exists('Mail_mimePart')) { - $GLOBALS['SOAP_options']['Mime'] = 1; - define('MAIL_MIMEPART_CRLF', "\r\n"); -} - -@include_once 'Net/DIME.php'; -if (class_exists('Net_DIME_Message')) { - $GLOBALS['SOAP_options']['DIME'] = 1; -} - -/** - * Enable debugging information? - * - * @global bool $GLOBALS['SOAP_DEBUG'] - * @name $SOAP_DEBUG - */ -$GLOBALS['SOAP_DEBUG'] = false; - -if (!function_exists('version_compare') || - version_compare(phpversion(), '4.1', '<')) { - die("requires PHP 4.1 or higher\n"); -} -if (version_compare(phpversion(), '4.1', '>=') && - version_compare(phpversion(), '4.2', '<')) { - define('FLOAT', 'double'); -} else { - define('FLOAT', 'float'); -} if (!defined('INF')) { define('INF', 1.8e307); @@ -89,8 +36,8 @@ if (!defined('NAN')) { define('NAN', 0.0); } -define('SOAP_LIBRARY_VERSION', '0.8.0RC4'); -define('SOAP_LIBRARY_NAME', 'PEAR-SOAP 0.8.0RC4-devel'); +define('SOAP_LIBRARY_VERSION', '0.12.0'); +define('SOAP_LIBRARY_NAME', 'PEAR-SOAP 0.12.0-beta'); // Set schema version. define('SOAP_XML_SCHEMA_VERSION', 'http://www.w3.org/2001/XMLSchema'); @@ -104,6 +51,7 @@ define('SCHEMA_DISCO', 'http://schemas.xmlsoap.org/disco/'); define('SCHEMA_DISCO_SCL', 'http://schemas.xmlsoap.org/disco/scl/'); define('SCHEMA_SOAP', 'http://schemas.xmlsoap.org/wsdl/soap/'); +define('SCHEMA_SOAP12', 'http://schemas.xmlsoap.org/wsdl/soap12/'); define('SCHEMA_SOAP_HTTP', 'http://schemas.xmlsoap.org/soap/http'); define('SCHEMA_WSDL_HTTP', 'http://schemas.xmlsoap.org/wsdl/http/'); define('SCHEMA_MIME', 'http://schemas.xmlsoap.org/wsdl/mime/'); @@ -114,24 +62,11 @@ define('SCHEMA_REF', 'http://schemas.xmlsoap.org/ws/2002/04/refere define('SOAP_DEFAULT_ENCODING', 'UTF-8'); +/** + * @package SOAP + */ class SOAP_Base_Object extends PEAR { - /** - * Store debugging information in $_debug_data? - * - * @see $debug_data, SOAP_Base - * @var boolean $_debug_flag - */ - var $_debug_flag = false; - - /** - * String containing debugging information if $_debug_flag is true. - * - * @access public - * @see $debug_flag, SOAP_Base - * @var string $_debug_data - */ - var $_debug_data = ''; /** * Supported encodings, limited by XML extension. @@ -157,14 +92,11 @@ class SOAP_Base_Object extends PEAR /** * Constructor. * - * @see $debug_data, _debug() - * * @param string $faultcode Error code. */ function SOAP_Base_Object($faultcode = 'Client') { $this->_myfaultcode = $faultcode; - $this->_debug_flag = $GLOBALS['SOAP_DEBUG']; parent::PEAR('SOAP_Fault'); } @@ -174,10 +106,6 @@ class SOAP_Base_Object extends PEAR * Please refer to the SOAP definition for an impression of what a certain * parameter stands for. * - * Use $debug_flag to store errors to the member variable $debug_data - * - * @see $debug_flag, $debug_data, SOAP_Fault - * * @param string|object $str Error message or object. * @param string $detail Detailed error message. * @param string $actorURI @@ -190,50 +118,34 @@ class SOAP_Base_Object extends PEAR $mode = null, $options = null, $skipmsg = false) { // Pass through previous faults. - $is_instance = isset($this); + $is_instance = isset($this) && is_a($this, 'SOAP_Base_Object'); if (is_object($str)) { - $fault =& $str; + $fault = $str; } else { if (!$code) { $code = $is_instance ? $this->_myfaultcode : 'Client'; } - $fault =& new SOAP_Fault($str, - $code, - $actorURI, - $detail, - $mode, - $options); + require_once 'SOAP/Fault.php'; + $fault = new SOAP_Fault($str, $code, $actorURI, $detail, $mode, + $options); } if ($is_instance) { - $this->fault =& $fault; + $this->fault = $fault; } return $fault; } - function __isfault() + function _isfault() { return $this->fault != null; } - function &__getfault() + function &_getfault() { return $this->fault; } - /** - * Adds a string to the debug data. - * - * @param string $string Debugging message. - */ - function _debug($string) - { - if ($this->_debug_flag) { - $this->_debug_data .= get_class($this) . ': ' . - str_replace('>', ">\r\n", $string) . "\n"; - } - } - } /** @@ -254,9 +166,9 @@ class SOAP_Base extends SOAP_Base_Object 'http://www.w3.org/2001/XMLSchema' => array( 'string' => 'string', 'boolean' => 'boolean', - 'float' => FLOAT, - 'double' => FLOAT, - 'decimal' => FLOAT, + 'float' => 'float', + 'double' => 'float', + 'decimal' => 'float', 'duration' => 'integer', 'dateTime' => 'string', 'time' => 'string', @@ -284,7 +196,8 @@ class SOAP_Base extends SOAP_Base_Object 'integer' => 'integer', 'nonPositiveInteger' => 'integer', 'negativeInteger' => 'integer', - 'long' => 'integer', + // longs (64bit ints) are not supported cross-platform. + 'long' => 'string', 'int' => 'integer', 'short' => 'integer', 'byte' => 'string', @@ -303,8 +216,8 @@ class SOAP_Base extends SOAP_Base_Object 'int' => 'integer', 'boolean' => 'boolean', 'string' => 'string', - 'double' => FLOAT, - 'float' => FLOAT, + 'double' => 'float', + 'float' => 'float', 'dateTime' => 'string', 'timeInstant' => 'string', 'base64Binary' => 'string', @@ -325,8 +238,18 @@ class SOAP_Base extends SOAP_Base_Object */ var $_defaultObjectClassname = 'stdClass'; - // Load namespace URIs into an array of URI => prefix. + /** + * Hash with used namespaces. + * + * @var array + */ var $_namespaces; + + /** + * The default namespace. + * + * @var string + */ var $_namespace; var $_xmlEntities = array('&' => '&', @@ -337,7 +260,7 @@ class SOAP_Base extends SOAP_Base_Object var $_doconversion = false; - var $__attachments = array(); + var $_attachments = array(); var $_wsdl = null; @@ -355,8 +278,6 @@ class SOAP_Base extends SOAP_Base_Object /** * Constructor. * - * @see $debug_data, _debug() - * * @param string $faultcode Error code. */ function SOAP_Base($faultcode = 'Client') @@ -365,30 +286,60 @@ class SOAP_Base extends SOAP_Base_Object $this->_resetNamespaces(); } - function _resetNamespaces() + /** + * Sets the SOAP-ENV prefix and returns the current value. + * + * @access public + * + * @param string SOAP-ENV prefix + * + * @return string current SOAP-ENV prefix. + */ + function SOAPENVPrefix($prefix = null) { - $this->_namespaces = array( - 'http://schemas.xmlsoap.org/soap/envelope/' => 'SOAP-ENV', - 'http://www.w3.org/2001/XMLSchema' => 'xsd', - 'http://www.w3.org/2001/XMLSchema-instance' => 'xsi', - 'http://schemas.xmlsoap.org/soap/encoding/' => 'SOAP-ENC'); + static $_soapenv_prefix = 'SOAP-ENV'; + if (!is_null($prefix)) { + $_soapenv_prefix = $prefix; + } + return $_soapenv_prefix; } - function isValidField($fieldname, $type) + /** + * Sets the SOAP-ENC prefix and returns the current value. + * + * @access public + * + * @param string SOAP-ENC prefix + * + * @return string current SOAP-ENC prefix. + */ + function SOAPENCPrefix($prefix = null) { - foreach($this->_wsdl->complexTypes as $nss) - { - foreach($nss as $ns) - { - if (array_key_exists($fieldname, $ns['elements'])) - { - return true; - } - } + static $_soapenv_prefix = 'SOAP-ENC'; + if (!is_null($prefix)) { + $_soapenv_prefix = $prefix; } - return false; + return $_soapenv_prefix; + } + + /** + * Sets the default namespace. + * + * @param string $namespace The default namespace. + */ + function setDefaultNamespace($namespace) + { + $this->_namespace = $namespace; } + function _resetNamespaces() + { + $this->_namespaces = array( + 'http://schemas.xmlsoap.org/soap/envelope/' => SOAP_BASE::SOAPENVPrefix(), + 'http://www.w3.org/2001/XMLSchema' => 'xsd', + 'http://www.w3.org/2001/XMLSchema-instance' => 'xsi', + 'http://schemas.xmlsoap.org/soap/encoding/' => SOAP_BASE::SOAPENCPrefix()); + } /** * Sets the schema version used in the SOAP message. @@ -432,28 +383,35 @@ class SOAP_Base extends SOAP_Base_Object return null; } - function _isSoapValue(&$value) - { - return is_a($value, 'SOAP_Value'); - } - - function _serializeValue(&$value, $name = '', $type = false, - $elNamespace = null, $typeNamespace = null, + /** + * Serializes a value, array or object according to the rules set by this + * object. + * + * @see SOAP_Value + * + * @param mixed $value The actual value. + * @param QName $name The value name. + * @param QName $type The value type. + * @param array $options A list of encoding and serialization options. + * @param array $attributes A hash of additional attributes. + * @param string $artype The type of any array elements. + */ + function _serializeValue($value, $name = null, $type = null, $options = array(), $attributes = array(), $artype = '') { - $namespaces = array(); - $arrayType = $array_depth = $xmlout_value = null; - $typePrefix = $elPrefix = $xmlout_offset = $xmlout_arrayType = ''; + $namespaces = array(); + $arrayType = $array_depth = $xmlout_value = null; + $typePrefix = $elPrefix = $xmlout_arrayType = ''; $xmlout_type = $xmlns = $ptype = $array_type_ns = ''; - if (!$name || is_numeric($name)) { - $name = 'item'; + if (!$name->name || is_numeric($name->name)) { + $name->name = 'item'; } if ($this->_wsdl) { list($ptype, $arrayType, $array_type_ns, $array_depth) - = $this->_wsdl->getSchemaType($type, $name, $typeNamespace); + = $this->_wsdl->getSchemaType($type, $name); } if (!$arrayType) { @@ -463,22 +421,20 @@ class SOAP_Base extends SOAP_Base_Object $ptype = $this->_getType($value); } if (!$type) { - $type = $ptype; + $type = new QName($ptype); } if (strcasecmp($ptype, 'Struct') == 0 || - strcasecmp($type, 'Struct') == 0) { + strcasecmp($type->name, 'Struct') == 0) { // Struct - $vars = null; - if (is_object($value)) { - $vars = get_object_vars($value); - } else { - $vars = &$value; - } + $vars = is_object($value) ? get_object_vars($value) : $value; if (is_array($vars)) { foreach (array_keys($vars) as $k) { // Hide private vars. - if ($k[0] == '_') continue; + if ($k[0] == '_') { + continue; + } + if (is_object($vars[$k])) { if (is_a($vars[$k], 'SOAP_Value')) { $xmlout_value .= $vars[$k]->serialize($this); @@ -486,23 +442,19 @@ class SOAP_Base extends SOAP_Base_Object // XXX get the members and serialize them instead // converting to an array is more overhead than we // should really do. - $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), $k, false, $this->_section5 ? null : $elNamespace); + $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), new QName($k, $this->_section5 ? null : $name->namepace), null, $options); } } else { - if ($this->isValidField($k, $type)) - { - $xmlout_value .= $this->_serializeValue($vars[$k], $k, false, $this->_section5 ? null : $elNamespace); - } + $xmlout_value .= $this->_serializeValue($vars[$k], new QName($k, $this->_section5 ? null : $name->namespace), false, $options); } } } } elseif (strcasecmp($ptype, 'Array') == 0 || - strcasecmp($type, 'Array') == 0) { + strcasecmp($type->name, 'Array') == 0) { // Array. - $typeNamespace = SOAP_SCHEMA_ENCODING; - $orig_type = $type; - $type = 'Array'; + $type = new QName('Array', SOAP_SCHEMA_ENCODING); $numtypes = 0; + $value = (array)$value; // XXX this will be slow on larger arrays. Basically, it flattens // arrays to allow us to serialize multi-dimensional arrays. We // only do this if arrayType is set, which will typically only @@ -516,7 +468,7 @@ class SOAP_Base extends SOAP_Base_Object $array_type = $array_type_prefix = ''; if ($numtypes != 1) { - $arrayTypeQName =& new QName($arrayType); + $arrayTypeQName = new QName($arrayType); $arrayType = $arrayTypeQName->name; $array_types = array(); $array_val = null; @@ -524,7 +476,7 @@ class SOAP_Base extends SOAP_Base_Object // Serialize each array element. $ar_size = count($value); foreach ($value as $array_val) { - if ($this->_isSoapValue($array_val)) { + if (is_a($array_val, 'SOAP_Value')) { $array_type = $array_val->type; $array_types[$array_type] = 1; $array_type_ns = $array_val->type_namespace; @@ -532,18 +484,19 @@ class SOAP_Base extends SOAP_Base_Object } else { $array_type = $this->_getType($array_val); $array_types[$array_type] = 1; - $xmlout_value .= $this->_serializeValue($array_val, 'item', $array_type, $this->_section5 ? null : $elNamespace); + if (empty($options['keep_arrays_flat'])) { + $xmlout_value .= $this->_serializeValue($array_val, new QName('item', $this->_section5 ? null : $name->namespace), new QName($array_type), $options); + } else { + $xmlout_value .= $this->_serializeValue($array_val, $name, new QName($array_type), $options, $attributes); + } } } - $xmlout_offset = ' SOAP-ENC:offset="[0]"'; if (!$arrayType) { - /* $numtypes = count($array_types); if ($numtypes == 1) { $arrayType = $array_type; } - */ // Using anyType is more interoperable. if ($array_type == 'Struct') { $array_type = ''; @@ -565,57 +518,63 @@ class SOAP_Base extends SOAP_Base_Object $array_type_prefix = $this->_getNamespacePrefix($array_type_ns); } elseif (isset($this->_typemap[$this->_XMLSchemaVersion][$arrayType])) { $array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion]; + } elseif (isset($this->_typemap[SOAP_SCHEMA_ENCODING][$arrayType])) { + $array_type_prefix = SOAP_BASE::SOAPENCPrefix(); } if ($array_type_prefix) { $arrayType = $array_type_prefix . ':' . $arrayType; } } - $xmlout_arrayType = ' SOAP-ENC:arrayType="' . $arrayType; + $xmlout_arrayType = ' ' . SOAP_BASE::SOAPENCPrefix() + . ':arrayType="' . $arrayType; if ($array_depth != null) { for ($i = 0; $i < $array_depth; $i++) { $xmlout_arrayType .= '[]'; } } $xmlout_arrayType .= "[$ar_size]\""; - } elseif ($this->_isSoapValue($value)) { + } elseif (is_a($value, 'SOAP_Value')) { $xmlout_value = $value->serialize($this); - } elseif ($type == 'string') { + } elseif ($type->name == 'string') { $xmlout_value = htmlspecialchars($value); - } elseif ($type == 'rawstring') { - $xmlout_value =& $value; - } elseif ($type == 'boolean') { + } elseif ($type->name == 'rawstring') { + $xmlout_value = $value; + } elseif ($type->name == 'boolean') { $xmlout_value = $value ? 'true' : 'false'; } else { - $xmlout_value =& $value; + $xmlout_value = $value; } // Add namespaces. - if ($elNamespace) { - $elPrefix = $this->_getNamespacePrefix($elNamespace); + if ($name->namespace) { + $elPrefix = $this->_getNamespacePrefix($name->namespace); if ($elPrefix) { - $xmlout_name = "$elPrefix:$name"; + $xmlout_name = $elPrefix . ':' . $name->name; } else { - $xmlout_name = $name; + $xmlout_name = $name->name; } } else { - $xmlout_name = $name; + $xmlout_name = $name->name; } - if ($typeNamespace) { - $typePrefix = $this->_getNamespacePrefix($typeNamespace); + if ($type->namespace) { + $typePrefix = false; + if (empty($options['no_type_prefix'])) { + $typePrefix = $this->_getNamespacePrefix($type->namespace); + } if ($typePrefix) { - $xmlout_type = "$typePrefix:$type"; + $xmlout_type = $typePrefix . ':' . $type->name; } else { - $xmlout_type = $type; + $xmlout_type = $type->name; } - } elseif ($type && - isset($this->_typemap[$this->_XMLSchemaVersion][$type])) { + } elseif ($type->name && + isset($this->_typemap[$this->_XMLSchemaVersion][$type->name])) { $typePrefix = $this->_namespaces[$this->_XMLSchemaVersion]; if ($typePrefix) { - $xmlout_type = "$typePrefix:$type"; + $xmlout_type = $typePrefix . ':' . $type->name; } else { - $xmlout_type = $type; + $xmlout_type = $type->name; } } @@ -623,8 +582,8 @@ class SOAP_Base extends SOAP_Base_Object $xml_attr = ''; if (count($attributes)) { foreach ($attributes as $k => $v) { - $kqn =& new QName($k); - $vqn =& new QName($v); + $kqn = new QName($k); + $vqn = new QName($v); $xml_attr .= ' ' . $kqn->fqn() . '="' . $vqn->fqn() . '"'; } } @@ -632,7 +591,7 @@ class SOAP_Base extends SOAP_Base_Object // Store the attachment for mime encoding. if (isset($options['attachment']) && !PEAR::isError($options['attachment'])) { - $this->__attachments[] = $options['attachment']; + $this->_attachments[] = $options['attachment']; } if ($this->_section5) { @@ -644,8 +603,10 @@ class SOAP_Base extends SOAP_Base_Object "$xml_attr xsi:nil=\"true\"/>"; } else { $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" . - "$xmlout_offset$xml_attr>$xmlout_value"; + "$xml_attr>$xmlout_value"; } + } elseif ($type->name == 'Array' && !empty($options['keep_arrays_flat'])) { + $xml = $xmlout_value; } else { if (is_null($xmlout_value)) { $xml = "\r\n<$xmlout_name$xmlns$xml_attr/>"; @@ -661,16 +622,12 @@ class SOAP_Base extends SOAP_Base_Object /** * Converts a PHP type to a SOAP type. * - * @access private - * - * @param string $value The value to inspect. + * @param mixed $value The value to inspect. * * @return string The value's SOAP type. */ - function _getType(&$value) + function _getType($value) { - global $SOAP_OBJECT_STRUCT, $SOAP_RAW_CONVERT; - $type = gettype($value); switch ($type) { case 'object': @@ -685,26 +642,22 @@ class SOAP_Base extends SOAP_Base_Object // Hashes are always handled as structs. if ($this->_isHash($value)) { $type = 'Struct'; - } else { - $ar_size = count($value); + break; + } + if (count($value) > 1) { + // For non-wsdl structs that are all the same type reset($value); - $key1 = key($value); - if ($ar_size > 0 && is_a($key1, 'SOAP_Value')) { - // FIXME: for non-wsdl structs that are all the same type - $key2 = key($value); - if ($ar_size > 1 && - $this->_isSoapValue($key1) && - $this->_isSoapValue($key2) && - $key1->name != $key2->name) { - // This is a struct, not an array. - $type = 'Struct'; - } else { - $type = 'Array'; - } - } else { - $type = 'Array'; + $value1 = next($value); + $value2 = next($value); + if (is_a($value1, 'SOAP_Value') && + is_a($value2, 'SOAP_Value') && + $value1->name != $value2->name) { + // This is a struct, not an array. + $type = 'Struct'; + break; } } + $type = 'Array'; break; case 'integer': @@ -725,30 +678,6 @@ class SOAP_Base extends SOAP_Base_Object break; case 'string': - if ($SOAP_RAW_CONVERT) { - if (is_numeric($value)) { - if (strstr($value, '.')) { - $type = 'float'; - } else { - $type = 'int'; - } - } else { - if (SOAP_Type_hexBinary::is_hexbin($value)) { - $type = 'hexBinary'; - } else { - if ($this->_isBase64($value)) { - $type = 'base64Binary'; - } else { - $dt =& new SOAP_Type_dateTime($value); - if ($dt->toUnixtime() != -1) { - $type = 'dateTime'; - } - } - } - } - } - break; - default: break; } @@ -756,32 +685,28 @@ class SOAP_Base extends SOAP_Base_Object return $type; } - function _multiArrayType(&$value, &$type, &$size, &$xml) + function _multiArrayType($value, &$type, &$size, &$xml) { - $sz = count($value); if (is_array($value)) { // Seems we have a multi dimensional array, figure it out if we // do. - $c = count($value); - for ($i = 0; $i < $c; $i++) { + for ($i = 0, $c = count($value); $i < $c; ++$i) { $this->_multiArrayType($value[$i], $type, $size, $xml); } + $sz = count($value); if ($size) { - $size = $sz. ',' . $size; + $size = $sz . ',' . $size; } else { $size = $sz; } - return 1; + } elseif (is_object($value)) { + $type = $value->type; + $xml .= $value->serialize($this); } else { - if (is_object($value)) { - $type = $value->type; - $xml .= $value->serialize($this); - } else { - $type = $this->_getType($value); - $xml .= $this->_serializeValue($value, 'item', $type); - } + $type = $this->_getType($value); + $xml .= $this->_serializeValue($value, new QName('item'), new QName($type)); } $size = null; @@ -789,23 +714,6 @@ class SOAP_Base extends SOAP_Base_Object } /** - * Returns whether a string is base64 encoded data. - * - * @param string $value The string to check. - * - * @return boolean True if the specified value seems to be base64 encoded. - */ - function _isBase64(&$value) - { - $l = strlen($value); - if ($l) { - return $value[$l - 1] == '=' && - preg_match('/[A-Za-z=\/\+]+/', $value); - } - return false; - } - - /** * Returns whether a type is a base64 type. * * @param string $type A type name. @@ -824,27 +732,15 @@ class SOAP_Base extends SOAP_Base_Object * * @return boolean True if the specified array is a hash. */ - function _isHash(&$a) + function _isHash($a) { - // I really dislike having to loop through this in PHP code, really - // large arrays will be slow. We need a C function to do this. - $names = array(); - $it = 0; - foreach ($a as $k => $v) { + foreach (array_keys($a) as $k) { // Checking the type is faster than regexp. - $t = gettype($k); - if ($t != 'integer') { + if (!is_int($k)) { return true; - } elseif ($this->_isSoapValue($v)) { - $names[$v->name] = 1; - } - // If someone has a large hash they should really be defining the - // type. - if ($it++ > 10) { - return false; } } - return count($names)>1; + return false; } function _un_htmlentities($string) @@ -854,14 +750,18 @@ class SOAP_Base extends SOAP_Base_Object return strtr($string, $trans_tbl); } - function &_decode(&$soapval) + /** + * Converts a SOAP_Value object into a PHP value. + */ + function _decode($soapval) { - global $SOAP_OBJECT_STRUCT; - - if (!$this->_isSoapValue($soapval)) { + if (!is_a($soapval, 'SOAP_Value')) { return $soapval; - } elseif (is_array($soapval->value)) { - if ($SOAP_OBJECT_STRUCT && $soapval->type != 'Array') { + } + + if (is_array($soapval->value)) { + $isstruct = $soapval->type != 'Array'; + if ($isstruct) { $classname = $this->_defaultObjectClassname; if (isset($this->_type_translation[$soapval->tqn->fqn()])) { // This will force an error in PHP if the class does not @@ -881,15 +781,13 @@ class SOAP_Base extends SOAP_Base_Object } } } - $return =& new $classname; + $return = new $classname; } else { $return = array(); } - $counter = 1; - $isstruct = !$SOAP_OBJECT_STRUCT || !is_array($return); foreach ($soapval->value as $item) { - if (is_object($return)) { + if ($isstruct) { if ($this->_wsdl) { // Get this child's WSDL information. // /$soapval->ns/$soapval->type/$item->ns/$item->name @@ -902,29 +800,33 @@ class SOAP_Base extends SOAP_Base_Object $item->type = $child_type; } } - if (!$isstruct || $item->type == 'Array') { + if ($item->type == 'Array') { if (isset($return->{$item->name}) && is_object($return->{$item->name})) { - $return->{$item->name} =& $this->_decode($item); + $return->{$item->name} = $this->_decode($item); } elseif (isset($return->{$item->name}) && is_array($return->{$item->name})) { $return->{$item->name}[] = $this->_decode($item); + } elseif (isset($return->{$item->name})) { + $return->{$item->name} = array( + $return->{$item->name}, + $this->_decode($item) + ); } elseif (is_array($return)) { - $return[] =& $this->_decode($item); + $return[] = $this->_decode($item); } else { - $return->{$item->name} =& $this->_decode($item); + $return->{$item->name} = $this->_decode($item); } } elseif (isset($return->{$item->name})) { - $isstruct = false; + $d = $this->_decode($item); if (count(get_object_vars($return)) == 1) { - $d =& $this->_decode($item); + $isstruct = false; $return = array($return->{$item->name}, $d); } else { - $d =& $this->_decode($item); $return->{$item->name} = array($return->{$item->name}, $d); } } else { - $return->{$item->name} =& $this->_decode($item); + $return->{$item->name} = $this->_decode($item); } // Set the attributes as members in the class. if (method_exists($return, '__set_attribute')) { @@ -935,7 +837,7 @@ class SOAP_Base extends SOAP_Base_Object } } } else { - if ($soapval->arrayType && $this->_isSoapValue($item)) { + if ($soapval->arrayType && is_a($item, 'SOAP_Value')) { if ($this->_isBase64Type($item->type) && !$this->_isBase64Type($soapval->arrayType)) { // Decode the value if we're losing the base64 @@ -944,15 +846,7 @@ class SOAP_Base extends SOAP_Base_Object } $item->type = $soapval->arrayType; } - if (!$isstruct) { - $return[] = $this->_decode($item); - } elseif (isset($return[$item->name])) { - $isstruct = false; - $d =& $this->_decode($item); - $return = array($return[$item->name], $d); - } else { - $return[$item->name] = $this->_decode($item); - } + $return[] = $this->_decode($item); } } @@ -971,45 +865,42 @@ class SOAP_Base extends SOAP_Base_Object // If we can, set variable type. settype($soapval->value, $this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]); + } elseif ($soapval->type == 'Struct') { + $soapval->value = null; } - if ($this->_isBase64Type($soapval->type)) { - return base64_decode($soapval->value); - } else { - return $soapval->value; - } + return $soapval->value; } /** * Creates the SOAP envelope with the SOAP envelop data. * - * @access private - * - * @param - * @param array $headers - * @param string $encoding - * @param array $options + * @param SOAP_Value $method SOAP_Value instance with the method name as + * the name, and the method arguments as the + * value. + * @param array $headers A list of additional SOAP_Header objects. + * @param string $encoding The charset of the SOAP message. + * @param array $options A list of encoding/serialization options. * - * @return string + * @return string The complete SOAP message. */ - function _makeEnvelope(&$method, &$headers, - $encoding = SOAP_DEFAULT_ENCODING, - $options = array()) + function makeEnvelope($method, $headers, $encoding = SOAP_DEFAULT_ENCODING, + $options = array()) { $smsg = $header_xml = $ns_string = ''; if ($headers) { - $c = count($headers); - for ($i = 0; $i < $c; $i++) { + for ($i = 0, $c = count($headers); $i < $c; $i++) { $header_xml .= $headers[$i]->serialize($this); } - $header_xml = "\r\n$header_xml\r\n\r\n"; + $header_xml = sprintf("<%s:Header>\r\n%s\r\n\r\n", + SOAP_BASE::SOAPENVPrefix(), $header_xml, + SOAP_BASE::SOAPENVPrefix()); } if (!isset($options['input']) || $options['input'] == 'parse') { if (is_array($method)) { - $c = count($method); - for ($i = 0; $i < $c; $i++) { + for ($i = 0, $c = count($method); $i < $c; $i++) { $smsg .= $method[$i]->serialize($this); } } else { @@ -1018,53 +909,55 @@ class SOAP_Base extends SOAP_Base_Object } else { $smsg = $method; } - $body = "\r\n" . $smsg . "\r\n\r\n"; + $body = sprintf("<%s:Body>%s\r\n\r\n", + SOAP_BASE::SOAPENVPrefix(), $smsg, + SOAP_BASE::SOAPENVPrefix()); foreach ($this->_namespaces as $k => $v) { - $ns_string .= " xmlns:$v=\"$k\"\r\n"; + $ns_string .= "\r\n " . sprintf('xmlns:%s="%s"', $v, $k); } if ($this->_namespace) { - $ns_string .= " xmlns=\"{$this->_namespace}\"\r\n"; + $ns_string .= "\r\n " . sprintf('xmlns="%s"', $this->_namespace); } - /* If 'use' == 'literal', we do not put in the encodingStyle. This is + /* If 'use' == 'literal', do not put in the encodingStyle. This is * denoted by $this->_section5 being false. 'use' can be defined at a * more granular level than we are dealing with here, so this does not * work for all services. */ - $xml = "\r\n\r\n". - "_section5 ? ' SOAP-ENV:encodingStyle="' . SOAP_SCHEMA_ENCODING . '"' : ''). - ">\r\n". - "$header_xml$body\r\n"; + $xml = sprintf('%s<%s:Envelope%s', + $encoding, "\r\n", SOAP_BASE::SOAPENVPrefix(), + $ns_string); + if ($this->_section5) { + $xml .= "\r\n " . sprintf('%s:encodingStyle="%s"', + SOAP_BASE::SOAPENVPrefix(), + SOAP_SCHEMA_ENCODING); + } + $xml .= sprintf('>%s%s%s' . "\r\n", + "\r\n", $header_xml, $body, SOAP_BASE::SOAPENVPrefix()); return $xml; } - function _makeMimeMessage(&$xml, $encoding = SOAP_DEFAULT_ENCODING) + function _makeMimeMessage($xml, $encoding = SOAP_DEFAULT_ENCODING) { - global $SOAP_options; - - if (!isset($SOAP_options['Mime'])) { - return $this->_raiseSoapFault('Mime is not installed'); + if (!@include_once 'Mail/mimePart.php') { + return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed'); } - // Encode any attachments. - // See http://www.w3.org/TR/SOAP-attachments + // Encode any attachments. See http://www.w3.org/TR/SOAP-attachments // Now we have to mime encode the message. - $params = array('content_type' => 'multipart/related; type=text/xml'); - $msg =& new Mail_mimePart('', $params); + $params = array('content_type' => 'multipart/related; type="text/xml"'); + $msg = new Mail_mimePart('', $params); // Add the xml part. $params['content_type'] = 'text/xml'; $params['charset'] = $encoding; - $params['encoding'] = 'base64'; $msg->addSubPart($xml, $params); // Add the attachements - $c = count($this->__attachments); - for ($i = 0; $i < $c; $i++) { - $attachment =& $this->__attachments[$i]; - $msg->addSubPart($attachment['body'], $attachment); + for ($i = 0, $c = count($this->_attachments); $i < $c; ++$i) { + $msg->addSubPart($this->_attachments[$i]['body'], + $this->_attachments[$i]); } return $msg->encode(); @@ -1073,25 +966,22 @@ class SOAP_Base extends SOAP_Base_Object // TODO: this needs to be used from the Transport system. function _makeDIMEMessage($xml) { - global $SOAP_options; - - if (!isset($SOAP_options['DIME'])) { - return $this->_raiseSoapFault('DIME is not installed'); + if (!@include_once 'Net/DIME.php') { + return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed'); } - // Encode any attachments. - // See http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt + // Encode any attachments. See + // http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt // Now we have to DIME encode the message - $dime =& new Net_DIME_Message(); + $dime = new Net_DIME_Message(); $msg = $dime->encodeData($xml, SOAP_ENVELOP, null, NET_DIME_TYPE_URI); // Add the attachments. - $c = count($this->__attachments); + $c = count($this->_attachments); for ($i = 0; $i < $c; $i++) { - $attachment =& $this->__attachments[$i]; - $msg .= $dime->encodeData($attachment['body'], - $attachment['content_type'], - $attachment['cid'], + $msg .= $dime->encodeData($this->_attachments[$i]['body'], + $this->_attachments[$i]['content_type'], + $this->_attachments[$i]['cid'], NET_DIME_TYPE_MEDIA); } $msg .= $dime->endMessage(); @@ -1101,11 +991,8 @@ class SOAP_Base extends SOAP_Base_Object function _decodeMimeMessage(&$data, &$headers, &$attachments) { - global $SOAP_options; - - if (!isset($SOAP_options['Mime'])) { - $this->_raiseSoapFault('Mime Unsupported, install PEAR::Mail::Mime', '', '', 'Server'); - return; + if (!@include_once 'Mail/mimeDecode.php') { + return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed'); } $params['include_bodies'] = true; @@ -1113,7 +1000,7 @@ class SOAP_Base extends SOAP_Base_Object $params['decode_headers'] = true; // Lame thing to have to do for decoding. - $decoder =& new Mail_mimeDecode($data); + $decoder = new Mail_mimeDecode($data); $structure = $decoder->decode($params); if (isset($structure->body)) { @@ -1125,21 +1012,21 @@ class SOAP_Base extends SOAP_Base_Object $data = $structure->parts[0]->body; $headers = array_merge($structure->headers, $structure->parts[0]->headers); - if (count($structure->parts) > 1) { - $mime_parts = array_splice($structure->parts,1); - // Prepare the parts for the SOAP parser. - - $c = count($mime_parts); - for ($i = 0; $i < $c; $i++) { - $p =& $mime_parts[$i]; - if (isset($p->headers['content-location'])) { - // TODO: modify location per SwA note section 3 - // http://www.w3.org/TR/SOAP-attachments - $attachments[$p->headers['content-location']] = $p->body; - } else { - $cid = 'cid:' . substr($p->headers['content-id'], 1, -1); - $attachments[$cid] = $p->body; - } + if (count($structure->parts) <= 1) { + return; + } + + $mime_parts = array_splice($structure->parts, 1); + // Prepare the parts for the SOAP parser. + for ($i = 0, $c = count($mime_parts); $i < $c; $i++) { + $p = $mime_parts[$i]; + if (isset($p->headers['content-location'])) { + // TODO: modify location per SwA note section 3 + // http://www.w3.org/TR/SOAP-attachments + $attachments[$p->headers['content-location']] = $p->body; + } else { + $cid = 'cid:' . substr($p->headers['content-id'], 1, -1); + $attachments[$cid] = $p->body; } } @@ -1151,16 +1038,13 @@ class SOAP_Base extends SOAP_Base_Object function _decodeDIMEMessage(&$data, &$headers, &$attachments) { - global $SOAP_options; - - if (!isset($SOAP_options['DIME'])) { - $this->_raiseSoapFault('DIME Unsupported, install PEAR::Net::DIME', '', '', 'Server'); - return; + if (!@include_once 'Net/DIME.php') { + return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed'); } // This SHOULD be moved to the transport layer, e.g. PHP itself should // handle parsing DIME ;) - $dime =& new Net_DIME_Message(); + $dime = new Net_DIME_Message(); $err = $dime->decodeData($data); if (PEAR::isError($err)) { $this->_raiseSoapFault('Failed to decode the DIME message!', '', '', 'Server'); @@ -1185,9 +1069,18 @@ class SOAP_Base extends SOAP_Base_Object } } - function __set_type_translation($type, $class = null) + /** + * Explicitly sets the translation for a specific class. + * + * Auto translation works for all cases, but opens ANY class in the script + * to be used as a data type, and may not be desireable. + * + * @param string $type A SOAP type. + * @param string $class A PHP class name. + */ + function setTypeTranslation($type, $class = null) { - $tq =& new QName($type); + $tq = new QName($type); if (!$class) { $class = $tq->name; } @@ -1199,7 +1092,6 @@ class SOAP_Base extends SOAP_Base_Object /** * Class used to handle QNAME values in XML. * - * @access public * @package SOAP * @author Shane Caraveo Conversion to PEAR and updates */ @@ -1207,7 +1099,7 @@ class QName { var $name = ''; var $ns = ''; - var $namespace=''; + var $namespace = ''; function QName($name, $namespace = '') { diff --git a/thirdparty/pear/SOAP/Client.php b/thirdparty/pear/SOAP/Client.php old mode 100644 new mode 100755 index 81c63f3..86d77a8 --- a/thirdparty/pear/SOAP/Client.php +++ b/thirdparty/pear/SOAP/Client.php @@ -22,6 +22,7 @@ * @link http://pear.php.net/package/SOAP */ +/** SOAP_Value */ require_once 'SOAP/Value.php'; require_once 'SOAP/Base.php'; require_once 'SOAP/Transport.php'; @@ -35,8 +36,11 @@ require_once 'SOAP/Parser.php'; // NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer // these two are BC/FC handlers for call in PHP4/5 +/** + * @package SOAP + */ if (!class_exists('SOAP_Client_Overload')) { - if (substr(phpversion(), 0, 1) == 5) { + if (substr(zend_version(), 0, 1) > 1) { class SOAP_Client_Overload extends SOAP_Base { function __call($method, $args) { @@ -68,6 +72,10 @@ if (!class_exists('SOAP_Client_Overload')) { * $soapclient = new SOAP_Client( string path [ , boolean wsdl] ); * echo $soapclient->call( string methodname [ , array parameters] ); * + * or, if using PHP 5+ or the overload extension: + * $soapclient = new SOAP_Client( string path [ , boolean wsdl] ); + * echo $soapclient->methodname( [ array parameters] ); + * * * Originally based on SOAPx4 by Dietrich Ayala * http://dietrich.ganx4.com/soapx4 @@ -92,76 +100,93 @@ class SOAP_Client extends SOAP_Client_Overload * https://www.example.com/soap/server.php * mailto:soap@example.com * - * @see SOAP_Client() - * @var $_endpoint string + * @see SOAP_Client() + * @var string */ var $_endpoint = ''; /** * The SOAP PORT name that is used by the client. * - * @var $_portName string + * @var string */ var $_portName = ''; /** * Endpoint type e.g. 'wdsl'. * - * @var $__endpointType string + * @var string */ - var $__endpointType = ''; + var $_endpointType = ''; /** * The received xml. * - * @var $xml string + * @var string */ var $xml; /** * The outgoing and incoming data stream for debugging. * - * @var $wire string + * @var string */ var $wire; - var $__last_request = null; - var $__last_response = null; + + /** + * The outgoing data stream for debugging. + * + * @var string + */ + var $_last_request = null; + + /** + * The incoming data stream for debugging. + * + * @var string + */ + var $_last_response = null; /** * Options. * - * @var $__options array + * @var array */ - var $__options = array('trace'=>0); + var $_options = array('trace' => false); /** * The character encoding used for XML parser, etc. * - * @var $_encoding string + * @var string */ var $_encoding = SOAP_DEFAULT_ENCODING; /** * The array of SOAP_Headers that we are sending. * - * @var $headersOut array + * @var array */ var $headersOut = null; /** * The headers we recieved back in the response. * - * @var $headersIn array + * @var array */ var $headersIn = null; /** * Options for the HTTP_Request class (see HTTP/Request.php). * - * @var $__proxy_params array + * @var array */ - var $__proxy_params = array(); + var $_proxy_params = array(); + /** + * The SOAP_Transport instance. + * + * @var SOAP_Transport + */ var $_soap_transport = null; /** @@ -169,20 +194,22 @@ class SOAP_Client extends SOAP_Client_Overload * * @access public * - * @param string $endpoint An URL. - * @param boolean $wsdl Whether the endpoint is a WSDL file. - * @param string $portName - * @param array $proxy_params Options for the HTTP_Request class (see - * HTTP/Request.php) + * @param string $endpoint An URL. + * @param boolean $wsdl Whether the endpoint is a WSDL file. + * @param string $portName The service's port name to use. + * @param array $proxy_params Options for the HTTP_Request class + * @see HTTP_Request + * @param boolean|string $cache Use WSDL caching? The cache directory if + * a string. */ function SOAP_Client($endpoint, $wsdl = false, $portName = false, - $proxy_params = array()) + $proxy_params = array(), $cache = false) { parent::SOAP_Base('Client'); $this->_endpoint = $endpoint; $this->_portName = $portName; - $this->__proxy_params = $proxy_params; + $this->_proxy_params = $proxy_params; // This hack should perhaps be removed as it might cause unexpected // behaviour. @@ -192,10 +219,11 @@ class SOAP_Client extends SOAP_Client_Overload // make values if ($wsdl) { - $this->__endpointType = 'wsdl'; + $this->_endpointType = 'wsdl'; // instantiate wsdl class - $this->_wsdl =& new SOAP_WSDL($this->_endpoint, - $this->__proxy_params); + $this->_wsdl = new SOAP_WSDL($this->_endpoint, + $this->_proxy_params, + $cache); if ($this->_wsdl->fault) { $this->_raiseSoapFault($this->_wsdl->fault); } @@ -206,8 +234,8 @@ class SOAP_Client extends SOAP_Client_Overload { $this->xml = null; $this->wire = null; - $this->__last_request = null; - $this->__last_response = null; + $this->_last_request = null; + $this->_last_response = null; $this->headersIn = null; $this->headersOut = null; } @@ -242,18 +270,18 @@ class SOAP_Client extends SOAP_Client_Overload * 'mustunderstand', and 'actor' to send * as a header. */ - function addHeader(&$soap_value) + function addHeader($soap_value) { // Add a new header to the message. if (is_a($soap_value, 'SOAP_Header')) { - $this->headersOut[] =& $soap_value; + $this->headersOut[] = $soap_value; } elseif (is_array($soap_value)) { // name, value, namespace, mustunderstand, actor - $this->headersOut[] =& new SOAP_Header($soap_value[0], - null, - $soap_value[1], - $soap_value[2], - $soap_value[3]);; + $this->headersOut[] = new SOAP_Header($soap_value[0], + null, + $soap_value[1], + $soap_value[2], + $soap_value[3]); } else { $this->_raiseSoapFault('Invalid parameter provided to addHeader(). Must be an array or a SOAP_Header.'); } @@ -268,82 +296,90 @@ class SOAP_Client extends SOAP_Client_Overload * it is overloaded, the soapaction parameter is ignored and MUST be * placed in the options array. This is done to provide backwards * compatibility with current clients, but may be removed in the future. - * The currently supported values are:
    -     *   namespace
    -     *   soapaction
    -     *   timeout (HTTP socket timeout)
    -     *   transfer-encoding (SMTP, Content-Transfer-Encoding: header)
    -     *   from (SMTP, From: header)
    -     *   subject (SMTP, Subject: header)
    -     *   headers (SMTP, hash of extra SMTP headers)
    -     * 
    + * The currently supported values are: + * - 'namespace' + * - 'soapaction' + * - 'timeout': HTTP socket timeout + * - 'transfer-encoding': SMTP transport, Content-Transfer-Encoding: header + * - 'from': SMTP transport, From: header + * - 'subject': SMTP transport, Subject: header + * - 'headers': SMTP transport, hash of extra SMTP headers + * - 'attachments': what encoding to use for attachments (Mime, Dime) + * - 'trace': whether to trace the SOAP communication + * - 'style': 'document' or 'rpc'; when set to 'document' the parameters + * are not wrapped inside a tag with the SOAP action name + * - 'use': 'literal' for literal encoding, anything else for section 5 + * encoding; when set to 'literal' SOAP types will be omitted. + * - 'keep_arrays_flat': use the tag name multiple times for each element + * when passing in an array in literal mode + * - 'no_type_prefix': supress adding of the namespace prefix * * @access public * * @param string $method The method to call. * @param array $params The method parameters. - * @param string|array $namespace Namespace or hash with options. + * @param string|array $namespace Namespace or hash with options. Note: + * most options need to be repeated for + * SOAP_Value instances. * @param string $soapAction * * @return mixed The method result or a SOAP_Fault on error. */ - function &call($method, &$params, $namespace = false, $soapAction = false) + function call($method, $params, $namespace = false, $soapAction = false) { $this->headersIn = null; - $this->__last_request = null; - $this->__last_response = null; + $this->_last_request = null; + $this->_last_response = null; $this->wire = null; $this->xml = null; - $soap_data =& $this->__generate($method, $params, $namespace, $soapAction); + $soap_data = $this->_generate($method, $params, $namespace, $soapAction); if (PEAR::isError($soap_data)) { - $fault =& $this->_raiseSoapFault($soap_data); + $fault = $this->_raiseSoapFault($soap_data); return $fault; } - // __generate() may have changed the endpoint if the WSDL has more + // _generate() may have changed the endpoint if the WSDL has more // than one service, so we need to see if we need to generate a new // transport to hook to a different URI. Since the transport protocol // can also change, we need to get an entirely new object. This could // probably be optimized. if (!$this->_soap_transport || $this->_endpoint != $this->_soap_transport->url) { - $this->_soap_transport =& SOAP_Transport::getTransport($this->_endpoint); + $this->_soap_transport = SOAP_Transport::getTransport($this->_endpoint); if (PEAR::isError($this->_soap_transport)) { - $fault =& $this->_soap_transport; + $fault = $this->_raiseSoapFault($this->_soap_transport); $this->_soap_transport = null; - $fault =& $this->_raiseSoapFault($fault); return $fault; } } $this->_soap_transport->encoding = $this->_encoding; // Send the message. - $transport_options = array_merge_recursive($this->__proxy_params, - $this->__options); + $transport_options = array_merge_recursive($this->_proxy_params, + $this->_options); $this->xml = $this->_soap_transport->send($soap_data, $transport_options); // Save the wire information for debugging. - if ($this->__options['trace'] > 0) { - $this->__last_request =& $this->_soap_transport->outgoing_payload; - $this->__last_response =& $this->_soap_transport->incoming_payload; - $this->wire = $this->__get_wire(); + if ($this->_options['trace']) { + $this->_last_request = $this->_soap_transport->outgoing_payload; + $this->_last_response = $this->_soap_transport->incoming_payload; + $this->wire = $this->getWire(); } if ($this->_soap_transport->fault) { - $fault =& $this->_raiseSoapFault($this->xml); + $fault = $this->_raiseSoapFault($this->xml); return $fault; } - $this->__attachments =& $this->_soap_transport->attachments; - $this->__result_encoding = $this->_soap_transport->result_encoding; - - if (isset($this->__options['result']) && - $this->__options['result'] != 'parse') { + if (isset($this->_options['result']) && + $this->_options['result'] != 'parse') { return $this->xml; } - $result = &$this->__parse($this->xml, $this->__result_encoding, $this->__attachments); + $this->__result_encoding = $this->_soap_transport->result_encoding; + $result = $this->parseResponse($this->xml, $this->__result_encoding, + $this->_soap_transport->attachments); return $result; } @@ -368,12 +404,12 @@ class SOAP_Client extends SOAP_Client_Overload function setOpt($category, $option, $value = null) { if (!is_null($value)) { - if (!isset($this->__options[$category])) { - $this->__options[$category] = array(); + if (!isset($this->_options[$category])) { + $this->_options[$category] = array(); } - $this->__options[$category][$option] = $value; + $this->_options[$category][$option] = $value; } else { - $this->__options[$category] = $option; + $this->_options[$category] = $option; } } @@ -391,7 +427,7 @@ class SOAP_Client extends SOAP_Client_Overload * * @param string $method The method to call. * @param array $params The method parameters. - * @param string $return_value Will get the method's return value + * @param mixed $return_value Will get the method's return value * assigned. * * @return boolean Always true. @@ -399,73 +435,121 @@ class SOAP_Client extends SOAP_Client_Overload function _call($method, $params, &$return_value) { // Overloading lowercases the method name, we need to look into the - // wsdl and try to find the correct method name to get the correct + // WSDL and try to find the correct method name to get the correct // case for the call. if ($this->_wsdl) { $this->_wsdl->matchMethod($method); } - $return_value =& $this->call($method, $params); + $return_value = $this->call($method, $params); return true; } - function &__getlastrequest() + /** + * Returns the XML content of the last SOAP request. + * + * @return string The last request. + */ + function getLastRequest() { - $request =& $this->__last_request; - return $request; + return $this->_last_request; } - function &__getlastresponse() + /** + * Returns the XML content of the last SOAP response. + * + * @return string The last response. + */ + function getLastResponse() { - $response =& $this->__last_response; - return $response; + return $this->_last_response; } - function __use($use) + /** + * Sets the SOAP encoding. + * + * The default encoding is section 5 encoded. + * + * @param string $use Either 'literal' or 'encoded' (section 5). + */ + function setUse($use) { - $this->__options['use'] = $use; + $this->_options['use'] = $use; } - function __style($style) + /** + * Sets the SOAP encoding style. + * + * The default style is rpc. + * + * @param string $style Either 'document' or 'rpc'. + */ + function setStyle($style) { - $this->__options['style'] = $style; + $this->_options['style'] = $style; } - function __trace($level) + /** + * Sets whether to trace the traffic on the transport level. + * + * @see getWire() + * + * @param boolean $trace + */ + function setTrace($trace) { - $this->__options['trace'] = $level; + $this->_options['trace'] = $trace; } - function &__generate($method, &$params, $namespace = false, - $soapAction = false) + /** + * Generates the complete XML SOAP message for an RPC call. + * + * @see call() + * + * @param string $method The method to call. + * @param array $params The method parameters. + * @param string|array $namespace Namespace or hash with options. Note: + * most options need to be repeated for + * SOAP_Value instances. + * @param string $soapAction + * + * @return string The SOAP message including envelope. + */ + function _generate($method, $params, $namespace = false, + $soapAction = false) { $this->fault = null; - $this->__options['input']='parse'; - $this->__options['result']='parse'; - $this->__options['parameters'] = false; + $this->_options['input'] = 'parse'; + $this->_options['result'] = 'parse'; + $this->_options['parameters'] = false; - if ($params && gettype($params) != 'array') { + if ($params && !is_array($params)) { $params = array($params); } - if (gettype($namespace) == 'array') { + if (is_array($namespace)) { + // Options passed as a hash. foreach ($namespace as $optname => $opt) { - $this->__options[strtolower($optname)] = $opt; - } - if (isset($this->__options['namespace'])) { - $namespace = $this->__options['namespace']; - } else { - $namespace = false; + $this->_options[strtolower($optname)] = $opt; } } else { // We'll place $soapAction into our array for usage in the // transport. - $this->__options['soapaction'] = $soapAction; - $this->__options['namespace'] = $namespace; + if ($soapAction) { + $this->_options['soapaction'] = $soapAction; + } + if ($namespace) { + $this->_options['namespace'] = $namespace; + } + } + if (isset($this->_options['namespace'])) { + $namespace = $this->_options['namespace']; + } else { + $namespace = false; } - if ($this->__endpointType == 'wsdl') { + if ($this->_endpointType == 'wsdl') { $this->_setSchemaVersion($this->_wsdl->xsd); // Get port name. @@ -473,36 +557,32 @@ class SOAP_Client extends SOAP_Client_Overload $this->_portName = $this->_wsdl->getPortName($method); } if (PEAR::isError($this->_portName)) { - $fault =& $this->_raiseSoapFault($this->_portName); - return $fault; + return $this->_raiseSoapFault($this->_portName); } // Get endpoint. $this->_endpoint = $this->_wsdl->getEndpoint($this->_portName); if (PEAR::isError($this->_endpoint)) { - $fault =& $this->_raiseSoapFault($this->_endpoint); - return $fault; + return $this->_raiseSoapFault($this->_endpoint); } // Get operation data. $opData = $this->_wsdl->getOperationData($this->_portName, $method); if (PEAR::isError($opData)) { - $fault =& $this->_raiseSoapFault($opData); - return $fault; + return $this->_raiseSoapFault($opData); } - $namespace = $opData['namespace']; - $this->__options['style'] = $opData['style']; - $this->__options['use'] = $opData['input']['use']; - $this->__options['soapaction'] = $opData['soapAction']; + $namespace = $opData['namespace']; + $this->_options['style'] = $opData['style']; + $this->_options['use'] = $opData['input']['use']; + $this->_options['soapaction'] = $opData['soapAction']; // Set input parameters. - if ($this->__options['input'] == 'parse') { - $this->__options['parameters'] = $opData['parameters']; + if ($this->_options['input'] == 'parse') { + $this->_options['parameters'] = $opData['parameters']; $nparams = array(); if (isset($opData['input']['parts']) && count($opData['input']['parts'])) { - $i = 0; foreach ($opData['input']['parts'] as $name => $part) { $xmlns = ''; $attrs = array(); @@ -518,14 +598,13 @@ class SOAP_Client extends SOAP_Client_Overload } else { // We now force an associative array for // parameters if using WSDL. - $fault =& $this->_raiseSoapFault("The named parameter $name is not in the call parameters."); - return $fault; + return $this->_raiseSoapFault("The named parameter $name is not in the call parameters."); } if (gettype($nparams[$name]) != 'object' || !is_a($nparams[$name], 'SOAP_Value')) { // Type is likely a qname, split it apart, and get // the type namespace from WSDL. - $qname =& new QName($part['type']); + $qname = new QName($part['type']); if ($qname->ns) { $type_namespace = $this->_wsdl->namespaces[$qname->ns]; } elseif (isset($part['namespace'])) { @@ -534,12 +613,11 @@ class SOAP_Client extends SOAP_Client_Overload $type_namespace = null; } $qname->namespace = $type_namespace; - $type = $qname->name; $pqname = $name; if ($xmlns) { $pqname = '{' . $xmlns . '}' . $name; } - $nparams[$name] =& new SOAP_Value($pqname, + $nparams[$name] = new SOAP_Value($pqname, $qname->fqn(), $nparams[$name], $attrs); @@ -556,117 +634,130 @@ class SOAP_Client extends SOAP_Client_Overload } // Serialize the message. - $this->_section5 = (!isset($this->__options['use']) || - $this->__options['use'] != 'literal'); + $this->_section5 = (!isset($this->_options['use']) || + $this->_options['use'] != 'literal'); - if (!isset($this->__options['style']) || - $this->__options['style'] == 'rpc') { - $this->__options['style'] = 'rpc'; + if (!isset($this->_options['style']) || + $this->_options['style'] == 'rpc') { + $this->_options['style'] = 'rpc'; $this->docparams = true; - $mqname =& new QName($method, $namespace); - $methodValue =& new SOAP_Value($mqname->fqn(), 'Struct', $params); - $soap_msg = $this->_makeEnvelope($methodValue, - $this->headersOut, - $this->_encoding, - $this->__options); + $mqname = new QName($method, $namespace); + $methodValue = new SOAP_Value($mqname->fqn(), 'Struct', $params, + array(), $this->_options); + $soap_msg = $this->makeEnvelope($methodValue, + $this->headersOut, + $this->_encoding, + $this->_options); } else { if (!$params) { - $mqname =& new QName($method, $namespace); - $mynull = null; - $params =& new SOAP_Value($mqname->fqn(), 'Struct', $mynull); - } elseif ($this->__options['input'] == 'parse') { + $mqname = new QName($method, $namespace); + $params = new SOAP_Value($mqname->fqn(), 'Struct', null); + } elseif ($this->_options['input'] == 'parse') { if (is_array($params)) { $nparams = array(); $keys = array_keys($params); foreach ($keys as $k) { if (gettype($params[$k]) != 'object') { - $nparams[] =& new SOAP_Value($k, - false, - $params[$k]); + $nparams[] = new SOAP_Value($k, + false, + $params[$k]); } else { $nparams[] =& $params[$k]; } } $params =& $nparams; } - if ($this->__options['parameters']) { - $mqname =& new QName($method, $namespace); - $params =& new SOAP_Value($mqname->fqn(), - 'Struct', - $params); + if ($this->_options['parameters']) { + $mqname = new QName($method, $namespace); + $params = new SOAP_Value($mqname->fqn(), + 'Struct', + $params); } } - $soap_msg = $this->_makeEnvelope($params, - $this->headersOut, - $this->_encoding, - $this->__options); + $soap_msg = $this->makeEnvelope($params, + $this->headersOut, + $this->_encoding, + $this->_options); } - unset($this->headersOut); + $this->headersOut = null; if (PEAR::isError($soap_msg)) { - $fault =& $this->_raiseSoapFault($soap_msg); - return $fault; + return $this->_raiseSoapFault($soap_msg); } // Handle MIME or DIME encoding. // TODO: DIME encoding should move to the transport, do it here for // now and for ease of getting it done. - if (count($this->__attachments)) { - if ((isset($this->__options['attachments']) && - $this->__options['attachments'] == 'Mime') || - isset($this->__options['Mime'])) { - $soap_msg =& $this->_makeMimeMessage($soap_msg, - $this->_encoding); + if (count($this->_attachments)) { + if ((isset($this->_options['attachments']) && + $this->_options['attachments'] == 'Mime') || + isset($this->_options['Mime'])) { + $soap_msg = $this->_makeMimeMessage($soap_msg, $this->_encoding); } else { // default is dime - $soap_msg =& $this->_makeDIMEMessage($soap_msg, - $this->_encoding); - $this->__options['headers']['Content-Type'] = 'application/dime'; + $soap_msg = $this->_makeDIMEMessage($soap_msg, $this->_encoding); + $this->_options['headers']['Content-Type'] = 'application/dime'; } if (PEAR::isError($soap_msg)) { - $fault =& $this->_raiseSoapFault($soap_msg); - return $fault; + return $this->_raiseSoapFault($soap_msg); } } // Instantiate client. if (is_array($soap_msg)) { - $soap_data =& $soap_msg['body']; + $soap_data = $soap_msg['body']; if (count($soap_msg['headers'])) { - if (isset($this->__options['headers'])) { - $this->__options['headers'] = array_merge($this->__options['headers'], $soap_msg['headers']); + if (isset($this->_options['headers'])) { + $this->_options['headers'] = array_merge($this->_options['headers'], $soap_msg['headers']); } else { - $this->__options['headers'] = $soap_msg['headers']; + $this->_options['headers'] = $soap_msg['headers']; } } } else { - $soap_data =& $soap_msg; + $soap_data = $soap_msg; } return $soap_data; } - function &__parse(&$response, $encoding, &$attachments) + /** + * Parses a SOAP response. + * + * @see SOAP_Parser:: + * + * @param string $response XML content of SOAP response. + * @param string $encoding Character set encoding, defaults to 'UTF-8'. + * @param array $attachments List of attachments. + */ + function parseResponse($response, $encoding, $attachments) { // Parse the response. - $response =& new SOAP_Parser($response, $encoding, $attachments); + $response = new SOAP_Parser($response, $encoding, $attachments); if ($response->fault) { - $fault =& $this->_raiseSoapFault($response->fault); + $fault = $this->_raiseSoapFault($response->fault); return $fault; } // Return array of parameters. - $return =& $response->getResponse(); - $headers =& $response->getHeaders(); + $return = $response->getResponse(); + $headers = $response->getHeaders(); if ($headers) { - $this->headersIn =& $this->__decodeResponse($headers, false); + $this->headersIn = $this->_decodeResponse($headers, false); } - $decoded = &$this->__decodeResponse($return); + $decoded = $this->_decodeResponse($return); return $decoded; } - function &__decodeResponse(&$response, $shift = true) + /** + * Converts a complex SOAP_Value into a PHP Array + * + * @param SOAP_Value $response Value object. + * @param boolean $shift + * + * @return array + */ + function _decodeResponse($response, $shift = true) { if (!$response) { $decoded = null; @@ -675,19 +766,19 @@ class SOAP_Client extends SOAP_Client_Overload // Check for valid response. if (PEAR::isError($response)) { - $fault =& $this->_raiseSoapFault($response); + $fault = $this->_raiseSoapFault($response); return $fault; } elseif (!is_a($response, 'soap_value')) { - $fault =& $this->_raiseSoapFault("Didn't get SOAP_Value object back from client"); + $fault = $this->_raiseSoapFault("Didn't get SOAP_Value object back from client"); return $fault; } // Decode to native php datatype. - $returnArray =& $this->_decode($response); + $returnArray = $this->_decode($response); // Fault? if (PEAR::isError($returnArray)) { - $fault =& $this->_raiseSoapFault($returnArray); + $fault = $this->_raiseSoapFault($returnArray); return $fault; } @@ -695,9 +786,10 @@ class SOAP_Client extends SOAP_Client_Overload strcasecmp(get_class($returnArray), 'stdClass') == 0) { $returnArray = get_object_vars($returnArray); } + if (is_array($returnArray)) { if (isset($returnArray['faultcode']) || - isset($returnArray['SOAP-ENV:faultcode'])) { + isset($returnArray[SOAP_BASE::SOAPENVPrefix().':faultcode'])) { $faultcode = $faultstring = $faultdetail = $faultactor = ''; foreach ($returnArray as $k => $v) { if (stristr($k, 'faultcode')) $faultcode = $v; @@ -705,7 +797,8 @@ class SOAP_Client extends SOAP_Client_Overload if (stristr($k, 'detail')) $faultdetail = $v; if (stristr($k, 'faultactor')) $faultactor = $v; } - $fault =& $this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode); + $fault = $this->_raiseSoapFault($faultstring, $faultdetail, + $faultactor, $faultcode); return $fault; } // Return array of return values. @@ -715,17 +808,27 @@ class SOAP_Client extends SOAP_Client_Overload } return $returnArray; } + return $returnArray; } - function __get_wire() + /** + * Returns the outgoing and incoming traffic on the transport level. + * + * Tracing has to be enabled. + * + * @see setTrace() + * + * @return string The complete traffic between the client and the server. + */ + function getWire() { - if ($this->__options['trace'] > 0 && - ($this->__last_request || $this->__last_response)) { + if ($this->_options['trace'] && + ($this->_last_request || $this->_last_response)) { return "OUTGOING:\n\n" . - $this->__last_request . + $this->_last_request . "\n\nINCOMING\n\n" . - preg_replace("/>\r\n<", $this->__last_response); + preg_replace("/>\r\n<", $this->_last_response); } return null; diff --git a/thirdparty/pear/SOAP/Copy of Base.php b/thirdparty/pear/SOAP/Copy of Base.php old mode 100644 new mode 100755 index a1ef47f..a1ef47f --- a/thirdparty/pear/SOAP/Copy of Base.php +++ b/thirdparty/pear/SOAP/Copy of Base.php diff --git a/thirdparty/pear/SOAP/Disco.php b/thirdparty/pear/SOAP/Disco.php old mode 100644 new mode 100755 index 482efd6..bc7f2b9 --- a/thirdparty/pear/SOAP/Disco.php +++ b/thirdparty/pear/SOAP/Disco.php @@ -22,8 +22,12 @@ * @link http://pear.php.net/package/SOAP */ +/** SOAP_Base */ require_once 'SOAP/Base.php'; +/** + * @package SOAP + */ class SOAP_DISCO_Server extends SOAP_Base_Object { var $namespaces = array(SCHEMA_WSDL => 'wsdl', SCHEMA_SOAP => 'soap'); @@ -90,7 +94,7 @@ class SOAP_DISCO_Server extends SOAP_Base_Object { $flipped = array_flip($this->soap_server->_namespaces); $this->namespaces[$this->_service_ns] = 'tns'; $this->namespaces[$flipped['xsd']] = 'xsd'; - $this->namespaces[$flipped['SOAP-ENC']] = 'SOAP-ENC'; + $this->namespaces[$flipped[SOAP_BASE::SOAPENCPrefix()]] = SOAP_BASE::SOAPENCPrefix(); } // DEFINITIONS @@ -154,8 +158,8 @@ class SOAP_DISCO_Server extends SOAP_Base_Object { $this->addMethodsFromMap($object->__dispatch_map, $namespace, get_class($object)); } } - if (isset($server->dispatch_map)) { - $this->addMethodsFromMap($server->dispatch_map, $namespace); + if (isset($this->soap_server->dispatch_map)) { + $this->addMethodsFromMap($this->soap_server->dispatch_map, $namespace); } // generate wsdl @@ -208,10 +212,10 @@ class SOAP_DISCO_Server extends SOAP_Base_Object { $el['attr']['type'] = $_vartypens . ':' . $_vartype; } else { $ctype['complexContent']['attr'] = ''; - $ctype['complexContent']['restriction']['attr']['base'] = 'SOAP-ENC:Array'; - foreach ($_vartype as $array_var => $array_type) { + $ctype['complexContent']['restriction']['attr']['base'] = SOAP_BASE::SOAPENCPrefix().':Array'; + foreach ($_vartype as $array_type) { list($_vartypens, $_vartype) = $this->_getTypeNs($array_type); - $ctype['complexContent']['restriction']['attribute']['attr']['ref'] = 'SOAP-ENC:arrayType'; + $ctype['complexContent']['restriction']['attribute']['attr']['ref'] = SOAP_BASE::SOAPENCPrefix().':arrayType'; $ctype['complexContent']['restriction']['attribute']['attr']['wsdl:arrayType'] = $_vartypens . ':' . $_vartype . '[]'; } } @@ -232,59 +236,58 @@ class SOAP_DISCO_Server extends SOAP_Base_Object { } else { $method_namespace = $namespace; } + // INPUT + $input_message = array('attr' => array('name' => $method_name . 'Request')); if (isset($method_types['in']) && is_array($method_types['in'])) { - $input_message =& $this->_wsdl['definitions']['message'][]; - $input_message['attr']['name'] = $method_name . 'Request'; foreach ($method_types['in'] as $name => $type) { list($typens, $type) = $this->_getTypeNs($type); - $part =& $input_message['part'][]; + $part = array(); $part['attr']['name'] = $name; $part['attr']['type'] = $typens . ':' . $type; + $input_message['part'][] = $part; } } + $this->_wsdl['definitions']['message'][] = $input_message; // OUTPUT + $output_message = array('attr' => array('name' => $method_name . 'Response')); if (isset($method_types['out']) && is_array($method_types['out'])) { - $output_message =& $this->_wsdl['definitions']['message'][]; - $output_message['attr']['name'] = $method_name . 'Response'; foreach ($method_types['out'] as $name => $type) { list($typens, $type) = $this->_getTypeNs($type); - $part =& $output_message['part'][]; + $part = array(); $part['attr']['name'] = $name; $part['attr']['type'] = $typens . ':' . $type; + $output_message['part'][] = $part; } } + $this->_wsdl['definitions']['message'][] = $output_message; // PORTTYPES - $operation =& $this->_wsdl['definitions']['portType']['operation'][]; + $operation = array(); $operation['attr']['name'] = $method_name; - // INPUT - $operation['input']['attr']['message'] = 'tns:' - . $input_message['attr']['name']; - + $operation['input']['attr']['message'] = 'tns:' . $input_message['attr']['name']; // OUTPUT - $operation['output']['attr']['message'] = 'tns:' - . $output_message['attr']['name']; + $operation['output']['attr']['message'] = 'tns:' . $output_message['attr']['name']; + $this->_wsdl['definitions']['portType']['operation'][] = $operation; // BINDING - $binding =& $this->_wsdl['definitions']['binding']['operation'][]; + $binding = array(); $binding['attr']['name'] = $method_name; $action = $method_namespace . '#' . ($classname ? $classname . '#' : '') . $method_name; $binding['soap:operation']['attr']['soapAction'] = $action; - // INPUT $binding['input']['attr'] = ''; $binding['input']['soap:body']['attr']['use'] = 'encoded'; $binding['input']['soap:body']['attr']['namespace'] = $method_namespace; $binding['input']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING; - // OUTPUT $binding['output']['attr'] = ''; $binding['output']['soap:body']['attr']['use'] = 'encoded'; $binding['output']['soap:body']['attr']['namespace'] = $method_namespace; $binding['output']['soap:body']['attr']['encodingStyle'] = SOAP_SCHEMA_ENCODING; + $this->_wsdl['definitions']['binding']['operation'][] = $binding; } } @@ -373,8 +376,8 @@ class SOAP_DISCO_Server extends SOAP_Base_Object { function _ifComplexTypeExists($typesArray, $type_name) { if (is_array($typesArray)) { - foreach ($typesArray as $index => $type_data) { - if ($typesArray[$index]['attr']['name'] == $type_name) { + foreach ($typesArray as $type_data) { + if ($type_data['attr']['name'] == $type_name) { return true; } } diff --git a/thirdparty/pear/SOAP/Fault.php b/thirdparty/pear/SOAP/Fault.php old mode 100644 new mode 100755 index 792b1ea..d9b1c9a --- a/thirdparty/pear/SOAP/Fault.php +++ b/thirdparty/pear/SOAP/Fault.php @@ -18,17 +18,28 @@ * @author Shane Caraveo Port to PEAR and more * @author Chuck Hagenbuch Maintenance * @author Jan Schneider Maintenance - * @copyright 2003-2005 The PHP Group + * @copyright 2003-2006 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ -require_once('PEAR.php'); +/** PEAR_Error */ +require_once 'PEAR.php'; /** - * SOAP_Fault * PEAR::Error wrapper used to match SOAP Faults to PEAR Errors * + * SOAP_Fault can provide a complete backtrace of the error. Revealing these + * details in a public web services is a bad idea because it can be used by + * attackers. Thus you have to enable backtrace information in SOAP_Fault + * responses by putting the following code in your script after your + * "require_once 'SOAP/Server.php';" line: + * + * + * $backtrace =& PEAR::getStaticProperty('SOAP_Fault', 'backtrace'); + * $backtrace = true; + * + * * @package SOAP * @access public * @author Shane Caraveo Port to PEAR and more @@ -36,101 +47,83 @@ require_once('PEAR.php'); */ class SOAP_Fault extends PEAR_Error { - /** - * Constructor - * - * @param string message string for fault - * @param mixed the faultcode - * @param mixed see PEAR::ERROR - * @param mixed see PEAR::ERROR - * @param array the userinfo array is used to pass in the - * SOAP actor and detail for the fault + * Constructor. + * + * @param string $faultstring Message string for fault. + * @param mixed $faultcode The faultcode. + * @param mixed $faultactor + * @param mixed $detail @see PEAR_Error + * @param array $mode @see PEAR_Error + * @param array $options @see PEAR_Error */ - function SOAP_Fault($faultstring = 'unknown error', $faultcode = 'Client', $faultactor=NULL, $detail=NULL, $mode = null, $options = null) + function SOAP_Fault($faultstring = 'unknown error', $faultcode = 'Client', + $faultactor = null, $detail = null, $mode = null, + $options = null) { parent::PEAR_Error($faultstring, $faultcode, $mode, $options, $detail); - if ($faultactor) $this->error_message_prefix = $faultactor; + if ($faultactor) { + $this->error_message_prefix = $faultactor; + } } - + /** - * message - * - * returns a SOAP_Message class that can be sent as a server response + * Returns a SOAP XML message that can be sent as a server response. * - * @return SOAP_Message - * @access public + * @return string */ - function message() + function message($encoding = SOAP_DEFAULT_ENCODING) { - $msg =& new SOAP_Base(); + $msg = new SOAP_Base(); $params = array(); - $params[] =& new SOAP_Value('faultcode', 'QName', 'SOAP-ENV:'.$this->code); - $params[] =& new SOAP_Value('faultstring', 'string', $this->message); - $params[] =& new SOAP_Value('faultactor', 'anyURI', $this->error_message_prefix); - if (isset($this->backtrace)) { - $params[] =& new SOAP_Value('detail', 'string', $this->backtrace); + $params[] = new SOAP_Value('faultcode', 'QName', SOAP_BASE::SOAPENVPrefix().':' . $this->code); + $params[] = new SOAP_Value('faultstring', 'string', $this->message); + $params[] = new SOAP_Value('faultactor', 'anyURI', $this->error_message_prefix); + if (PEAR::getStaticProperty('SOAP_Fault', 'backtrace') && + isset($this->backtrace)) { + $params[] = new SOAP_Value('detail', 'string', $this->backtrace); } else { - $params[] =& new SOAP_Value('detail', 'string', $this->userinfo); + $params[] = new SOAP_Value('detail', 'string', $this->userinfo); } - - $methodValue =& new SOAP_Value('{'.SOAP_ENVELOP.'}Fault', 'Struct', $params); - $headers = NULL; - return $msg->_makeEnvelope($methodValue, $headers); + + $methodValue = new SOAP_Value('{' . SOAP_ENVELOP . '}Fault', 'Struct', $params); + $headers = null; + return $msg->makeEnvelope($methodValue, $headers, $encoding); } - + /** - * getFault + * Returns a simple native PHP array containing the fault data. * - * returns a simple native php array containing the fault data - * - * @return array - * @access public + * @return array */ function getFault() { - global $SOAP_OBJECT_STRUCT; - if ($SOAP_OBJECT_STRUCT) { - $fault =& new stdClass(); - $fault->faultcode = $this->code; - $fault->faultstring = $this->message; - $fault->faultactor = $this->error_message_prefix; - $fault->detail = $this->userinfo; - return $fault; - } - return array( - 'faultcode' => $this->code, - 'faultstring' => $this->message, - 'faultactor' => $this->error_message_prefix, - 'detail' => $this->userinfo - ); + $fault = new stdClass(); + $fault->faultcode = $this->code; + $fault->faultstring = $this->message; + $fault->faultactor = $this->error_message_prefix; + $fault->detail = $this->userinfo; + return $fault; } - + /** - * getActor - * - * returns the SOAP actor for the fault + * Returns the SOAP actor for the fault. * - * @return string - * @access public + * @return string */ function getActor() { return $this->error_message_prefix; } - + /** - * getDetail - * - * returns the fault detail + * Returns the fault detail. * - * @return string - * @access public + * @return string */ function getDetail() { return $this->userinfo; } - + } -?> \ No newline at end of file diff --git a/thirdparty/pear/SOAP/Parser.php b/thirdparty/pear/SOAP/Parser.php old mode 100644 new mode 100755 index 02aed73..0cb89cb --- a/thirdparty/pear/SOAP/Parser.php +++ b/thirdparty/pear/SOAP/Parser.php @@ -41,58 +41,59 @@ class SOAP_Parser extends SOAP_Base { var $status = ''; var $position = 0; - var $pos_stat = 0; var $depth = 0; var $default_namespace = ''; var $message = array(); var $depth_array = array(); - var $previous_element = ''; - var $soapresponse = null; - var $soapheaders = null; var $parent = 0; var $root_struct_name = array(); var $header_struct_name = array(); var $curent_root_struct_name = ''; - var $entities = array ( '&' => '&', '<' => '<', '>' => '>', "'" => ''', '"' => '"' ); var $root_struct = array(); var $header_struct = array(); var $curent_root_struct = 0; var $references = array(); var $need_references = array(); - var $XMLSchemaVersion; - var $bodyDepth; // used to handle non-root elements before root body element /** - * SOAP_Parser constructor + * Used to handle non-root elements before root body element. * - * @param string xml content - * @param string xml character encoding, defaults to 'UTF-8' + * @var integer */ - function SOAP_Parser(&$xml, $encoding = SOAP_DEFAULT_ENCODING, $attachments = null) + var $bodyDepth; + + /** + * Constructor. + * + * @param string $xml XML content. + * @param string $encoding Character set encoding, defaults to 'UTF-8'. + * @param array $attachments List of attachments. + */ + function SOAP_Parser($xml, $encoding = SOAP_DEFAULT_ENCODING, + $attachments = null) { parent::SOAP_Base('Parser'); $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION); $this->attachments = $attachments; - // Check the xml tag for encoding. + // Check the XML tag for encoding. if (preg_match('/<\?xml[^>]+encoding\s*?=\s*?(\'([^\']*)\'|"([^"]*)")[^>]*?[\?]>/', $xml, $m)) { $encoding = strtoupper($m[2] ? $m[2] : $m[3]); } - // Determines where in the message we are - // (envelope,header,body,method). Check whether content has - // been read. + // Determine where in the message we are (envelope, header, body, + // method). Check whether content has been read. if (!empty($xml)) { - // Prepare the xml parser. + // Prepare the XML parser. $parser = xml_parser_create($encoding); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); xml_set_object($parser, $this); - xml_set_element_handler($parser, 'startElement', 'endElement'); - xml_set_character_data_handler($parser, 'characterData'); + xml_set_element_handler($parser, '_startElement', '_endElement'); + xml_set_character_data_handler($parser, '_characterData'); - // Some lame soap implementations add null bytes at the - // end of the soap stream, and expat choaks on that. + // Some lame SOAP implementations add nul bytes at the end of the + // SOAP stream, and expat chokes on that. if ($xml[strlen($xml) - 1] == 0) { $xml = trim($xml); } @@ -100,70 +101,101 @@ class SOAP_Parser extends SOAP_Base // Parse the XML file. if (!xml_parse($parser, $xml, true)) { $err = sprintf('XML error on line %d col %d byte %d %s', - xml_get_current_line_number($parser), - xml_get_current_column_number($parser), - xml_get_current_byte_index($parser), - xml_error_string(xml_get_error_code($parser))); - $this->_raiseSoapFault($err,htmlspecialchars($xml)); + xml_get_current_line_number($parser), + xml_get_current_column_number($parser), + xml_get_current_byte_index($parser), + xml_error_string(xml_get_error_code($parser))); + $this->_raiseSoapFault($err, htmlspecialchars($xml)); } xml_parser_free($parser); } } + /** + * Returns an array of responses. + * + * After parsing a SOAP message, use this to get the response. + * + * @return array + */ + function getResponse() + { + if (!empty($this->root_struct[0])) { + return $this->_buildResponse($this->root_struct[0]); + } else { + return $this->_raiseSoapFault('Cannot build response'); + } + } + + /** + * Returns an array of header responses. + * + * After parsing a SOAP message, use this to get the response. + * + * @return array + */ + function getHeaders() + { + if (!empty($this->header_struct[0])) { + return $this->_buildResponse($this->header_struct[0]); + } else { + // We don't fault if there are no headers; that can be handled by + // the application if necessary. + return null; + } + } /** - * domulti - * recurse to build a multi-dim array, used by buildResponse + * Recurses to build a multi dimensional array. * - * @access private + * @see _buildResponse() */ - function domulti($d, &$ar, &$r, &$v, $ad=0) + function _domulti($d, &$ar, &$r, &$v, $ad = 0) { if ($d) { - $this->domulti($d-1, $ar, $r[$ar[$ad]], $v, $ad+1); + $this->_domulti($d - 1, $ar, $r[$ar[$ad]], $v, $ad + 1); } else { $r = $v; } } /** - * buildResponse - * loop through msg, building response structures + * Loops through the message, building response structures. + * + * @param integer $pos Position. * - * @param int position * @return SOAP_Value - * @access private */ - function &buildResponse($pos) + function _buildResponse($pos) { $response = null; if (isset($this->message[$pos]['children'])) { $children = explode('|', $this->message[$pos]['children']); - foreach ($children as $c => $child_pos) { if ($this->message[$child_pos]['type'] != null) { - $response[] =& $this->buildResponse($child_pos); + $response[] = $this->_buildResponse($child_pos); } } - if (array_key_exists('arraySize', $this->message[$pos])) { + if (isset($this->message[$pos]['arraySize'])) { $ardepth = count($this->message[$pos]['arraySize']); if ($ardepth > 1) { $ar = array_pad(array(), $ardepth, 0); - if (array_key_exists('arrayOffset', $this->message[$pos])) { + if (isset($this->message[$pos]['arrayOffset'])) { for ($i = 0; $i < $ardepth; $i++) { $ar[$i] += $this->message[$pos]['arrayOffset'][$i]; } } $elc = count($response); for ($i = 0; $i < $elc; $i++) { - // recurse to build a multi-dimensional array - $this->domulti($ardepth, $ar, $newresp, $response[$i]); + // Recurse to build a multi dimensional array. + $this->_domulti($ardepth, $ar, $newresp, $response[$i]); - // increment our array pointers + // Increment our array pointers. $ad = $ardepth - 1; $ar[$ad]++; - while ($ad > 0 && $ar[$ad] >= $this->message[$pos]['arraySize'][$ad]) { + while ($ad > 0 && + $ar[$ad] >= $this->message[$pos]['arraySize'][$ad]) { $ar[$ad] = 0; $ad--; $ar[$ad]++; @@ -172,7 +204,7 @@ class SOAP_Parser extends SOAP_Base $response = $newresp; } elseif (isset($this->message[$pos]['arrayOffset']) && $this->message[$pos]['arrayOffset'][0] > 0) { - // check for padding + // Check for padding. $pad = $this->message[$pos]['arrayOffset'][0] + count($response) * -1; $response = array_pad($response, $pad, null); } @@ -182,79 +214,87 @@ class SOAP_Parser extends SOAP_Base // Build attributes. $attrs = array(); foreach ($this->message[$pos]['attrs'] as $atn => $atv) { - if (!strstr($atn, 'xmlns') && - !strpos($atn, ':')) { + if (!strstr($atn, 'xmlns') && !strpos($atn, ':')) { $attrs[$atn] = $atv; } } // Add current node's value. + $nqn = new QName($this->message[$pos]['name'], + $this->message[$pos]['namespace']); + $tqn = new QName($this->message[$pos]['type'], + $this->message[$pos]['type_namespace']); if ($response) { - $nqn =& new Qname($this->message[$pos]['name'], $this->message[$pos]['namespace']); - $tqn =& new Qname($this->message[$pos]['type'], $this->message[$pos]['type_namespace']); - $response =& new SOAP_Value($nqn->fqn(), $tqn->fqn(), $response, $attrs); + $response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), $response, + $attrs); if (isset($this->message[$pos]['arrayType'])) { $response->arrayType = $this->message[$pos]['arrayType']; } } else { - $nqn =& new Qname($this->message[$pos]['name'], $this->message[$pos]['namespace']); - $tqn =& new Qname($this->message[$pos]['type'], $this->message[$pos]['type_namespace']); - $response =& new SOAP_Value($nqn->fqn(), $tqn->fqn(), $this->message[$pos]['cdata'], $attrs); + // Check if value is an empty array + if ($tqn->name == 'Array') { + $response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), array(), + $attrs); + //if ($pos == 4) var_dump($this->message[$pos], $response); + } else { + $response = new SOAP_Value($nqn->fqn(), $tqn->fqn(), + $this->message[$pos]['cdata'], + $attrs); + } } - // handle header attribute that we need + // Handle header attribute that we need. if (array_key_exists('actor', $this->message[$pos])) { $response->actor = $this->message[$pos]['actor']; } if (array_key_exists('mustUnderstand', $this->message[$pos])) { $response->mustunderstand = $this->message[$pos]['mustUnderstand']; } + return $response; } /** - * startElement - * start-element handler used with xml parser - * - * @access private + * Start element handler used with the XML parser. */ - function startElement($parser, $name, $attrs) + function _startElement($parser, $name, $attrs) { - // position in a total number of elements, starting from 0 - // update class level pos + // Position in a total number of elements, starting from 0. + // Update class level position. $pos = $this->position++; - // and set mine - $this->message[$pos] = array(); - $this->message[$pos]['type'] = ''; - $this->message[$pos]['type_namespace'] = ''; - $this->message[$pos]['cdata'] = ''; - $this->message[$pos]['pos'] = $pos; - $this->message[$pos]['id'] = ''; + // And set mine. + $this->message[$pos] = array( + 'type' => '', + 'type_namespace' => '', + 'cdata' => '', + 'pos' => $pos, + 'id' => ''); - // parent/child/depth determinations + // Parent/child/depth determinations. - // depth = how many levels removed from root? - // set mine as current global depth and increment global depth value + // depth = How many levels removed from root? + // Set mine as current global depth and increment global depth value. $this->message[$pos]['depth'] = $this->depth++; - // else add self as child to whoever the current parent is + // Else add self as child to whoever the current parent is. if ($pos != 0) { - if (isset($this->message[$this->parent]['children'])) - $this->message[$this->parent]['children'] .= "|$pos"; - else + if (isset($this->message[$this->parent]['children'])) { + $this->message[$this->parent]['children'] .= '|' . $pos; + } else { $this->message[$this->parent]['children'] = $pos; + } } - // set my parent + // Set my parent. $this->message[$pos]['parent'] = $this->parent; - // set self as current value for this depth + // Set self as current value for this depth. $this->depth_array[$this->depth] = $pos; - // set self as current parent + // Set self as current parent. $this->parent = $pos; - $qname =& new QName($name); - // set status + $qname = new QName($name); + // Set status. if (strcasecmp('envelope', $qname->name) == 0) { $this->status = 'envelope'; } elseif (strcasecmp('header', $qname->name) == 0) { @@ -269,12 +309,13 @@ class SOAP_Parser extends SOAP_Base // Set method } elseif ($this->status == 'body') { // Is this element allowed to be a root? - // XXX this needs to be optimized, we loop through attrs twice now. + // TODO: this needs to be optimized, we loop through $attrs twice + // now. $can_root = $this->depth == $this->bodyDepth + 1; if ($can_root) { foreach ($attrs as $key => $value) { if (stristr($key, ':root') && !$value) { - $can_root = FALSE; + $can_root = false; } } } @@ -300,7 +341,7 @@ class SOAP_Parser extends SOAP_Base foreach ($attrs as $key => $value) { // If ns declarations, add to class level array of valid // namespaces. - $kqn =& new QName($key); + $kqn = new QName($key); if ($kqn->ns == 'xmlns') { $prefix = $kqn->name; @@ -321,14 +362,15 @@ class SOAP_Parser extends SOAP_Base // If it's a type declaration, set type. } elseif ($kqn->name == 'type') { - $vqn =& new QName($value); + $vqn = new QName($value); $this->message[$pos]['type'] = $vqn->name; $this->message[$pos]['type_namespace'] = $this->_getNamespaceForPrefix($vqn->ns); - // Should do something here with the namespace of - // specified type? + + // Should do something here with the namespace of specified + // type? } elseif ($kqn->name == 'arrayType') { - $vqn =& new QName($value); + $vqn = new QName($value); $this->message[$pos]['type'] = 'Array'; if (isset($vqn->arraySize)) { $this->message[$pos]['arraySize'] = $vqn->arraySize; @@ -367,7 +409,7 @@ class SOAP_Parser extends SOAP_Base } } // See if namespace is defined in tag. - if (array_key_exists('xmlns:' . $qname->ns, $attrs)) { + if (isset($attrs['xmlns:' . $qname->ns])) { $namespace = $attrs['xmlns:' . $qname->ns]; } elseif ($qname->ns && !$qname->namespace) { $namespace = $this->_getNamespaceForPrefix($qname->ns); @@ -380,23 +422,20 @@ class SOAP_Parser extends SOAP_Base } /** - * endElement - * end-element handler used with xml parser - * - * @access private + * End element handler used with the XML parser. */ - function endElement($parser, $name) + function _endElement($parser, $name) { - // Position of current element is equal to the last value left - // in depth_array for my depth. + // Position of current element is equal to the last value left in + // depth_array for my depth. $pos = $this->depth_array[$this->depth]; // Bring depth down a notch. $this->depth--; - $qname =& new QName($name); + $qname = new QName($name); // Get type if not explicitly declared in an xsi:type attribute. - // XXX check on integrating wsdl validation here + // TODO: check on integrating WSDL validation here. if ($this->message[$pos]['type'] == '') { if (isset($this->message[$pos]['children'])) { /* this is slow, need to look at some faster method @@ -411,7 +450,7 @@ class SOAP_Parser extends SOAP_Base } else { $parent = $this->message[$pos]['parent']; if ($this->message[$parent]['type'] == 'Array' && - array_key_exists('arrayType', $this->message[$parent])) { + isset($this->message[$parent]['arrayType'])) { $this->message[$pos]['type'] = $this->message[$parent]['arrayType']; } else { $this->message[$pos]['type'] = 'string'; @@ -432,7 +471,7 @@ class SOAP_Parser extends SOAP_Base // Handle any reverse references now. $idref = $this->message[$pos]['id']; - if ($idref != '' && array_key_exists($idref, $this->need_references)) { + if ($idref != '' && isset($this->need_references[$idref])) { foreach ($this->need_references[$idref] as $ref_pos) { // XXX is this stuff there already? $this->message[$ref_pos]['children'] = &$this->message[$pos]['children']; @@ -445,12 +484,9 @@ class SOAP_Parser extends SOAP_Base } /** - * characterData - * element content handler used with xml parser - * - * @access private + * Element content handler used with the XML parser. */ - function characterData($parser, $data) + function _characterData($parser, $data) { $pos = $this->depth_array[$this->depth]; if (isset($this->message[$pos]['cdata'])) { @@ -460,59 +496,4 @@ class SOAP_Parser extends SOAP_Base } } - /** - * Returns an array of responses. - * - * After parsing a SOAP message, use this to get the response. - * - * @return array - * @access public - */ - function &getResponse() - { - if (isset($this->root_struct[0]) && - $this->root_struct[0]) { - $response =& $this->buildResponse($this->root_struct[0]); - } else { - $response =& $this->_raiseSoapFault("couldn't build response"); - } - return $response; - } - - /** - * Returns an array of header responses. - * - * After parsing a SOAP message, use this to get the response. - * - * @return array - * @access public - */ - function &getHeaders() - { - if (isset($this->header_struct[0]) && - $this->header_struct[0]) { - $response = &$this->buildResponse($this->header_struct[0]); - } else { - // We don't fault if there are no headers; that can be handled by - // the application if necessary. - $response = null; - } - return $response; - } - - /** - * decodeEntities - * - * removes entities from text - * - * @param string - * @return string - * @access private - */ - function decodeEntities($text) - { - $trans_tbl = array_flip($this->entities); - return strtr($text, $trans_tbl); - } - } diff --git a/thirdparty/pear/SOAP/Server.php b/thirdparty/pear/SOAP/Server.php old mode 100644 new mode 100755 index 6796939..e051299 --- a/thirdparty/pear/SOAP/Server.php +++ b/thirdparty/pear/SOAP/Server.php @@ -78,11 +78,17 @@ class SOAP_Server extends SOAP_Base var $service = ''; //soapaction header var $method_namespace = null; - var $__options = array('use' => 'encoded', - 'style' => 'rpc', - 'parameters' => 0, - 'http_status_success' => '200 OK', - 'http_status_fault' => '500 SOAP Fault'); + + /** + * Options. + * + * @var array + */ + var $_options = array('use' => 'encoded', + 'style' => 'rpc', + 'parameters' => 0, + 'http_status_success' => '200 OK', + 'http_status_fault' => '500 SOAP Fault'); function SOAP_Server($options = null) { @@ -91,18 +97,18 @@ class SOAP_Server extends SOAP_Base if (is_array($options)) { if (isset($options['use'])) { - $this->__options['use'] = $options['use']; + $this->_options['use'] = $options['use']; } if (isset($options['style'])) { - $this->__options['style'] = $options['style']; + $this->_options['style'] = $options['style']; } if (isset($options['parameters'])) { - $this->__options['parameters'] = $options['parameters']; + $this->_options['parameters'] = $options['parameters']; } } // assume we encode with section 5 $this->_section5 = true; - if ($this->__options['use']=='literal') { + if ($this->_options['use'] == 'literal') { $this->_section5 = false; } } @@ -116,16 +122,16 @@ class SOAP_Server extends SOAP_Base * * @see http://www.php.net/set_error_handler */ - function _errorHandler($errno, $errmsg, $filename, $linenum, $vars) + function _errorHandler($errno, $errmsg, $filename, $linenum) { /* The error handler should ignore '0' errors, eg. hidden by @ - see * the set_error_handler manual page. (thanks to Alan Knowles). */ - if (!$errno || $errno == E_NOTICE || + if (!$errno || !error_reporting() || $errno == E_NOTICE || (defined('E_STRICT') && $errno == constant('E_STRICT'))) { - return; + return false; } - $this->fault =& new SOAP_Fault($errmsg, 'Server', 'PHP', "Errno: $errno\nFilename: $filename\nLineno: $linenum\n"); + $this->fault = new SOAP_Fault($errmsg, 'Server', 'PHP', "Errno: $errno\nFilename: $filename\nLineno: $linenum\n"); $this->_sendResponse(); exit; @@ -149,9 +155,16 @@ class SOAP_Server extends SOAP_Base /** - * Parses request and posts response. + * Parses the request and posts or returns the response. + * + * @param string $data The SOAP request data. + * @param string $endpoint The service endpoint. Determined automatically + * if left empty. + * @param boolean $test + * @param boolean $return Whether to return the SOAP response data + * instead of sending it to the client. */ - function service($data, $endpoint = '', $test = false) + function service($data, $endpoint = '', $test = false, $return = false) { $response = null; $attachments = array(); @@ -162,7 +175,7 @@ class SOAP_Server extends SOAP_Base if (!$test && !$this->endpoint) { /* We'll try to build our endpoint. */ $this->endpoint = 'http://' . $_SERVER['SERVER_NAME']; - if ($_SERVER['SERVER_PORT']) { + if (isset($_SERVER['SERVER_PORT'])) { $this->endpoint .= ':' . $_SERVER['SERVER_PORT']; } $this->endpoint .= $_SERVER['SCRIPT_NAME']; @@ -172,21 +185,21 @@ class SOAP_Server extends SOAP_Base * data as UTF-8 if no encoding set. */ if (isset($_SERVER['CONTENT_TYPE'])) { if (strcasecmp($_SERVER['CONTENT_TYPE'], 'application/dime') == 0) { - $this->_decodeDIMEMessage($data, $headers, $attachments); + $this->_decodeDIMEMessage($data, $this->headers, $attachments); $useEncoding = 'DIME'; } elseif (stristr($_SERVER['CONTENT_TYPE'], 'multipart/related')) { /* This is a mime message, let's decode it. */ $data = 'Content-Type: ' . stripslashes($_SERVER['CONTENT_TYPE']) . "\r\n\r\n" . $data; - $this->_decodeMimeMessage($data, $headers, $attachments); + $this->_decodeMimeMessage($data, $this->headers, $attachments); $useEncoding = 'Mime'; } - if (!isset($headers['content-type'])) { - $headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']); + if (!isset($this->headers['content-type'])) { + $this->headers['content-type'] = stripslashes($_SERVER['CONTENT_TYPE']); } if (!$this->fault && - !$this->_getContentEncoding($headers['content-type'])) { + !$this->_getContentEncoding($this->headers['content-type'])) { $this->xml_encoding = SOAP_DEFAULT_ENCODING; /* Found encoding we don't understand; return a fault. */ $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server'); @@ -196,11 +209,14 @@ class SOAP_Server extends SOAP_Base /* If this is not a POST with Content-Type text/xml, try to return a * WSDL file. */ if (!$this->fault && !$test && - ($_SERVER['REQUEST_METHOD'] != 'POST' || - strncmp($headers['content-type'], 'text/xml', 8) != 0)) { + ((isset($_SERVER['REQUEST_METHOD']) && + $_SERVER['REQUEST_METHOD'] != 'POST') || + (isset($this->headers['content-type']) && + strncmp($this->headers['content-type'], 'text/xml', 8) != 0))) { /* This is not possibly a valid SOAP request, try to return a WSDL * file. */ - $this->_raiseSoapFault('Invalid SOAP request, must be POST with content-type: text/xml, got: ' . (isset($headers['content-type']) ? $headers['content-type'] : 'Nothing!'), '', '', 'Server'); + $got = isset($this->headers['content-type']) ? $this->headers['content-type'] : 'Nothing!'; + $this->_raiseSoapFault('Invalid SOAP request, must be POST with content-type: text/xml, got: ' . $got, '', '', 'Server'); } if (!$this->fault) { @@ -210,7 +226,7 @@ class SOAP_Server extends SOAP_Base /* Handle Mime or DIME encoding. */ /* TODO: DIME decoding should move to the transport, do it here * for now and for ease of getting it done. */ - if (count($this->__attachments)) { + if (count($this->_attachments)) { if ($useEncoding == 'Mime') { $soap_msg = $this->_makeMimeMessage($soap_msg); } else { @@ -233,6 +249,13 @@ class SOAP_Server extends SOAP_Base } } + if ($return) { + if ($this->fault) { + $response = $this->fault->message(); + } + return $response; + } + $this->_sendResponse($response); } @@ -256,10 +279,10 @@ class SOAP_Server extends SOAP_Base } if ($this->fault) { - $hdrs = $hdrs_type . ' ' . $this->__options['http_status_fault'] . "\r\n"; - $response = $this->fault->message(); + $hdrs = $hdrs_type . ' ' . $this->_options['http_status_fault'] . "\r\n"; + $response = $this->fault->message($this->response_encoding); } else { - $hdrs = $hdrs_type . ' ' . $this->__options['http_status_success'] . "\r\n"; + $hdrs = $hdrs_type . ' ' . $this->_options['http_status_success'] . "\r\n"; } header($hdrs); @@ -282,9 +305,7 @@ class SOAP_Server extends SOAP_Base function &callMethod($methodname, &$args) { if ($this->callHandler) { - $ret = @call_user_func_array($this->callHandler, array($methodname, $args)); - return $ret; } @@ -293,16 +314,16 @@ class SOAP_Server extends SOAP_Base if ($args) { /* Call method with parameters. */ if (isset($this->soapobject) && is_object($this->soapobject)) { - $ret = @call_user_func_array(array(&$this->soapobject, $methodname), $args); + $ret = call_user_func_array(array(&$this->soapobject, $methodname), $args); } else { - $ret = @call_user_func_array($methodname, $args); + $ret = call_user_func_array($methodname, $args); } } else { /* Call method withour parameters. */ if (is_object($this->soapobject)) { - $ret = @call_user_func(array(&$this->soapobject, $methodname)); + $ret = call_user_func(array(&$this->soapobject, $methodname)); } else { - $ret = @call_user_func($methodname); + $ret = call_user_func($methodname); } } @@ -338,8 +359,8 @@ class SOAP_Server extends SOAP_Base if (is_a($method_response[$i], 'SOAP_Value')) { $return_val[] =& $method_response[$i++]; } else { - $qn =& new QName($key, $namespace); - $return_val[] =& new SOAP_Value($qn->fqn(), $type, $method_response[$i++]); + $qn = new QName($key, $namespace); + $return_val[] = new SOAP_Value($qn->fqn(), $type, $method_response[$i++]); } } } else { @@ -351,7 +372,7 @@ class SOAP_Server extends SOAP_Base $values = array_values($return_type); $return_type = $values[0]; } - $qn =& new QName($return_name, $namespace); + $qn = new QName($return_name, $namespace); $return_val = array(new SOAP_Value($qn->fqn(), $return_type, $method_response)); } } @@ -361,12 +382,18 @@ class SOAP_Server extends SOAP_Base function parseRequest($data = '', $attachments = null) { /* Parse response, get SOAP_Parser object. */ - $parser =& new SOAP_Parser($data, $this->xml_encoding, $attachments); - /* If fault occurred during message parsing. */ + $parser = new SOAP_Parser($data, $this->xml_encoding, $attachments); + if ($parser->fault) { + /* Fault occurred during message parsing. */ $this->fault = $parser->fault; return null; } + if (!count($parser->root_struct_name)) { + /* No method specified. */ + $this->_raiseSoapFault('No method specified in request.'); + return null; + } /* Handle message headers. */ $request_headers = $parser->getHeaders(); @@ -403,7 +430,7 @@ class SOAP_Server extends SOAP_Base /* If there are parameters to pass. */ $hr =& $this->callMethod($header_method, $header_data); if (PEAR::isError($hr)) { - $this->_raiseSoapDefault($hr); + $this->_raiseSoapFault($hr); return null; } $results = $this->buildResult($hr, $this->return_type, $header_method, $header_val->namespace); @@ -436,9 +463,9 @@ class SOAP_Server extends SOAP_Base $this->_raiseSoapFault($opData); return null; } - $this->__options['style'] = $opData['style']; - $this->__options['use'] = $opData['output']['use']; - $this->__options['parameters'] = $opData['parameters']; + $this->_options['style'] = $opData['style']; + $this->_options['use'] = $opData['output']['use']; + $this->_options['parameters'] = $opData['parameters']; } /* Does method exist? */ @@ -476,9 +503,9 @@ class SOAP_Server extends SOAP_Base return null; } - if ($this->__options['parameters'] || + if ($this->_options['parameters'] || !$method_response || - $this->__options['style']=='rpc') { + $this->_options['style'] == 'rpc') { /* Get the method result. */ if (is_null($method_response)) { $return_val = null; @@ -486,12 +513,12 @@ class SOAP_Server extends SOAP_Base $return_val = $this->buildResult($method_response, $this->return_type); } - $qn =& new QName($this->methodname . 'Response', $this->method_namespace); - $methodValue =& new SOAP_Value($qn->fqn(), 'Struct', $return_val); + $qn = new QName($this->methodname . 'Response', $this->method_namespace); + $methodValue = new SOAP_Value($qn->fqn(), 'Struct', $return_val); } else { $methodValue =& $method_response; } - return $this->_makeEnvelope($methodValue, $header_results, $this->response_encoding); + return $this->makeEnvelope($methodValue, $header_results, $this->response_encoding); } function &__decodeRequest($request, $shift = false) @@ -520,12 +547,12 @@ class SOAP_Server extends SOAP_Base if (is_object($requestArray) && get_class($requestArray) == 'stdClass') { $requestArray = get_object_vars($requestArray); - } elseif ($this->__options['style'] == 'document') { + } elseif ($this->_options['style'] == 'document') { $requestArray = array($requestArray); } if (is_array($requestArray)) { if (isset($requestArray['faultcode']) || - isset($requestArray['SOAP-ENV:faultcode'])) { + isset($requestArray[SOAP_BASE::SOAPENVPrefix().':faultcode'])) { $faultcode = $faultstring = $faultdetail = $faultactor = ''; foreach ($requestArray as $k => $v) { if (stristr($k, 'faultcode')) { @@ -586,8 +613,8 @@ class SOAP_Server extends SOAP_Base } /* If there are input parameters required. */ - if ($sig = $map['in']) { - $this->input_value = count($sig); + if ($map['in']) { + $this->input_value = count($map['in']); $this->return_type = false; if (is_array($map['out'])) { $this->return_type = count($map['out']) > 1 @@ -596,12 +623,12 @@ class SOAP_Server extends SOAP_Base } if (is_array($params)) { /* Validate the number of parameters. */ - if (count($params) == count($sig)) { + if (count($params) == count($map['in'])) { /* Make array of param types. */ foreach ($params as $param) { $p[] = strtolower($param->type); } - $sig_t = array_values($sig); + $sig_t = array_values($map['in']); /* Validate each param's type. */ for ($i = 0; $i < count($p); $i++) { /* If SOAP types do not match, it's still fine if the @@ -621,12 +648,12 @@ class SOAP_Server extends SOAP_Base return true; } else { /* Wrong number of params. */ - $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" required ' . count($sig) . ' and request provided ' . count($params), '', '', 'Client'); + $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" required ' . count($map['in']) . ' and request provided ' . count($params), '', '', 'Client'); return false; } } else { /* No params. */ - $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" requires ' . count($sig) . ' parameters, and request provided none.', '', '', 'Client'); + $this->_raiseSoapFault('SOAP request contained incorrect number of parameters. method "' . $this->methodname . '" requires ' . count($map['in']) . ' parameters, and request provided none.', '', '', 'Client'); return false; } } @@ -736,7 +763,7 @@ class SOAP_Server extends SOAP_Base */ function addToMap($methodname, $in, $out, $namespace = null, $alias = null) { - if (!function_exists($methodname)) { + if (!$this->callHandler && !function_exists($methodname)) { $this->_raiseSoapFault('Error mapping function', '', '', 'Server'); return false; } @@ -772,7 +799,7 @@ class SOAP_Server extends SOAP_Base function bindWSDL($wsdl_url) { /* Instantiate WSDL class. */ - $this->_wsdl =& new SOAP_WSDL($wsdl_url); + $this->_wsdl = new SOAP_WSDL($wsdl_url); if ($this->_wsdl->fault) { $this->_raiseSoapFault($this->_wsdl->fault); } @@ -781,11 +808,11 @@ class SOAP_Server extends SOAP_Base /** * @return void */ - function addObjectWSDL(&$wsdl_obj, $targetNamespace, $service_name, + function addObjectWSDL($wsdl_obj, $targetNamespace, $service_name, $service_desc = '') { if (!isset($this->_wsdl)) { - $this->_wsdl =& new SOAP_WSDL; + $this->_wsdl = new SOAP_WSDL; } $this->_wsdl->parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc); @@ -794,4 +821,5 @@ class SOAP_Server extends SOAP_Base $this->_raiseSoapFault($this->_wsdl->fault); } } + } diff --git a/thirdparty/pear/SOAP/Server/Email.php b/thirdparty/pear/SOAP/Server/Email.php old mode 100644 new mode 100755 index df40652..cf7be35 --- a/thirdparty/pear/SOAP/Server/Email.php +++ b/thirdparty/pear/SOAP/Server/Email.php @@ -19,6 +19,7 @@ * @link http://pear.php.net/package/SOAP */ +/** SOAP_Server */ require_once 'SOAP/Server.php'; require_once 'SOAP/Client.php'; require_once 'SOAP/Transport.php'; @@ -95,11 +96,9 @@ class SOAP_Server_Email extends SOAP_Server { /* If neither matches, we'll just try it anyway. */ if (stristr($data, 'Content-Type: application/dime')) { $this->_decodeDIMEMessage($data, $this->headers, $attachments); - $useEncoding = 'DIME'; } elseif (stristr($data, 'MIME-Version:')) { /* This is a mime message, let's decode it. */ $this->_decodeMimeMessage($data, $this->headers, $attachments); - $useEncoding = 'Mime'; } else { /* The old fallback, but decodeMimeMessage handles things fine. */ $this->_parseEmail($data); @@ -120,7 +119,7 @@ class SOAP_Server_Email extends SOAP_Server { $client =& new SOAP_Client(null); - return $client->__parse($data, $this->xml_encoding, $this->attachments); + return $client->parseResponse($data, $this->xml_encoding, $this->attachments); } function service(&$data, $endpoint = '', $send_response = true, @@ -145,8 +144,7 @@ class SOAP_Server_Email extends SOAP_Server { /* Get the character encoding of the incoming request treat incoming * data as UTF-8 if no encoding set. */ - if (!$response && - !$this->_getContentEncoding($this->headers['content-type'])) { + if (!$this->_getContentEncoding($this->headers['content-type'])) { $this->xml_encoding = SOAP_DEFAULT_ENCODING; /* An encoding we don't understand, return a fault. */ $this->_raiseSoapFault('Unsupported encoding, use one of ISO-8859-1, US-ASCII, UTF-8', '', '', 'Server'); @@ -161,13 +159,13 @@ class SOAP_Server_Email extends SOAP_Server { /* Handle Mime or DIME encoding. */ /* TODO: DIME Encoding should move to the transport, do it here * for now and for ease of getting it done. */ - if (count($this->__attachments)) { + if (count($this->_attachments)) { if ($useEncoding == 'Mime') { $soap_msg = $this->_makeMimeMessage($soap_msg); } else { /* Default is DIME. */ $soap_msg = $this->_makeDIMEMessage($soap_msg); - $header['Content-Type'] = 'application/dime'; + $soap_msg['headers']['Content-Type'] = 'application/dime'; } if (PEAR::isError($soap_msg)) { return $this->raiseSoapFault($soap_msg); diff --git a/thirdparty/pear/SOAP/Server/Email_Gateway.php b/thirdparty/pear/SOAP/Server/Email_Gateway.php old mode 100644 new mode 100755 index d573633..f31bd89 --- a/thirdparty/pear/SOAP/Server/Email_Gateway.php +++ b/thirdparty/pear/SOAP/Server/Email_Gateway.php @@ -19,6 +19,7 @@ * @link http://pear.php.net/package/SOAP */ +/** SOAP_Server_Email */ require_once 'SOAP/Server/Email.php'; require_once 'SOAP/Transport.php'; @@ -95,7 +96,7 @@ class SOAP_Server_Email_Gateway extends SOAP_Server_Email { foreach ($soap_transport->transport->attachments as $cid => $body) { $this->attachments[] = array('body' => $body, 'cid' => $cid, 'encoding' => 'base64'); } - if (count($this->__attachments)) { + if (count($this->_attachments)) { if ($useEncoding == 'Mime') { $soap_msg = $this->_makeMimeMessage($response); $options['headers']['MIME-Version'] = '1.0'; diff --git a/thirdparty/pear/SOAP/Server/TCP.php b/thirdparty/pear/SOAP/Server/TCP.php old mode 100644 new mode 100755 index 039a627..039a627 --- a/thirdparty/pear/SOAP/Server/TCP.php +++ b/thirdparty/pear/SOAP/Server/TCP.php diff --git a/thirdparty/pear/SOAP/Transport.php b/thirdparty/pear/SOAP/Transport.php old mode 100644 new mode 100755 index e9dada5..2641457 --- a/thirdparty/pear/SOAP/Transport.php +++ b/thirdparty/pear/SOAP/Transport.php @@ -15,7 +15,8 @@ * @package SOAP * @author Dietrich Ayala * @author Shane Caraveo - * @copyright 2003-2005 The PHP Group + * @author Jan Schneider + * @copyright 2003-2006 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ @@ -25,23 +26,101 @@ require_once 'SOAP/Base.php'; /** * SOAP Transport Layer * - * This layer can use different protocols dependant on the endpoint url provided - * no knowlege of the SOAP protocol is available at this level - * no knowlege of the transport protocols is available at this level + * This layer can use different protocols dependant on the endpoint url + * provided. + * + * No knowlege of the SOAP protocol is available at this level. + * No knowlege of the transport protocols is available at this level. * * @access public * @package SOAP * @author Shane Caraveo + * @author Jan Schneider */ -class SOAP_Transport +class SOAP_Transport extends SOAP_Base { - function &getTransport($url, $encoding = SOAP_DEFAULT_ENCODING) + /** + * Connection endpoint URL. + * + * @var string + */ + var $url = ''; + + /** + * Array containing urlparts. + * + * @see parse_url() + * + * @var mixed + */ + var $urlparts = null; + + /** + * Incoming payload. + * + * @var string + */ + var $incoming_payload = ''; + + /** + * Outgoing payload. + * + * @var string + */ + var $outgoing_payload = ''; + + /** + * Request encoding. + * + * @var string + */ + var $encoding = SOAP_DEFAULT_ENCODING; + + /** + * Response encoding. + * + * We assume UTF-8 if no encoding is set. + * + * @var string + */ + var $result_encoding = 'UTF-8'; + + /** + * Decoded attachments from the reponse. + * + * @var array + */ + var $attachments; + + /** + * Request User-Agent. + * + * @var string + */ + var $_userAgent = SOAP_LIBRARY_NAME; + + /** + * Sends and receives SOAP data. + * + * @access public + * @abstract + * + * @param string Outgoing SOAP data. + * @param array Options. + * + * @return string|SOAP_Fault + */ + function send($msg, $options = null) + { + return $this->_raiseSoapFault('SOAP_Transport::send() not implemented.'); + } + + function getTransport($url, $encoding = SOAP_DEFAULT_ENCODING) { $urlparts = @parse_url($url); if (!$urlparts['scheme']) { - $fault = SOAP_Base_Object::_raiseSoapFault("Invalid transport URI: $url"); - return $fault; + return SOAP_Base_Object::_raiseSoapFault("Invalid transport URI: $url"); } if (strcasecmp($urlparts['scheme'], 'mailto') == 0) { @@ -49,23 +128,20 @@ class SOAP_Transport } elseif (strcasecmp($urlparts['scheme'], 'https') == 0) { $transport_type = 'HTTP'; } else { - /* handle other transport types */ + /* Handle other transport types */ $transport_type = strtoupper($urlparts['scheme']); } - $transport_include = 'SOAP/Transport/' . $transport_type . '.php'; - $res = @include_once($transport_include); - if (!$res && !in_array($transport_include, get_included_files())) { - $fault = SOAP_Base_Object::_raiseSoapFault("No Transport for {$urlparts['scheme']}"); - return $fault; - } $transport_class = "SOAP_Transport_$transport_type"; if (!class_exists($transport_class)) { - $fault = SOAP_Base_Object::_raiseSoapFault("No Transport class $transport_class"); - return $fault; + if (!(@include_once('SOAP/Transport/' . basename($transport_type) . '.php'))) { + return SOAP_Base_Object::_raiseSoapFault("No Transport for {$urlparts['scheme']}"); + } + } + if (!class_exists($transport_class)) { + return SOAP_Base_Object::_raiseSoapFault("No Transport class $transport_class"); } - $t =& new $transport_class($url, $encoding); - return $t; + return new $transport_class($url, $encoding); } } diff --git a/thirdparty/pear/SOAP/Transport/HTTP.php b/thirdparty/pear/SOAP/Transport/HTTP.php old mode 100644 new mode 100755 index db4b607..4897141 --- a/thirdparty/pear/SOAP/Transport/HTTP.php +++ b/thirdparty/pear/SOAP/Transport/HTTP.php @@ -14,7 +14,8 @@ * @category Web Services * @package SOAP * @author Shane Caraveo - * @copyright 2003-2005 The PHP Group + * @author Jan Schneider + * @copyright 2003-2006 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ @@ -23,13 +24,13 @@ * HTTP Transport class * * @package SOAP - * @category Web_Services + * @category Web Services */ /** * Needed Classes */ -require_once 'SOAP/Base.php'; +require_once 'SOAP/Transport.php'; /** * HTTP Transport for SOAP @@ -37,10 +38,10 @@ require_once 'SOAP/Base.php'; * @access public * @package SOAP * @author Shane Caraveo + * @author Jan Schneider */ -class SOAP_Transport_HTTP extends SOAP_Base +class SOAP_Transport_HTTP extends SOAP_Transport { - /** * Basic Auth string. * @@ -63,49 +64,6 @@ class SOAP_Transport_HTTP extends SOAP_Base var $timeout = 4; /** - * Array containing urlparts - parse_url(). - * - * @var mixed - */ - var $urlparts = null; - - /** - * Connection endpoint - URL. - * - * @var string - */ - var $url = ''; - - /** - * Incoming payload. - * - * @var string - */ - var $incoming_payload = ''; - - /** - * HTTP-Request User-Agent. - * - * @var string - */ - var $_userAgent = SOAP_LIBRARY_NAME; - - /** - * HTTP encoding. - * - * @var string - */ - var $encoding = SOAP_DEFAULT_ENCODING; - - /** - * HTTP-Response Content-Type encoding. - * We assume UTF-8 if no encoding is set. - * - * @var string - */ - var $result_encoding = 'UTF-8'; - - /** * HTTP-Response Content-Type. */ var $result_content_type; @@ -133,14 +91,17 @@ class SOAP_Transport_HTTP extends SOAP_Base /** * Sends and receives SOAP data. * - * @param string Outgoing POST data. + * @access public + * + * @param string Outgoing SOAP data. * @param array Options. * * @return string|SOAP_Fault - * @access public */ - function send($msg, $options = null) + function send($msg, $options = array()) { + $this->fault = null; + if (!$this->_validateUrl()) { return $this->fault; } @@ -187,13 +148,45 @@ class SOAP_Transport_HTTP extends SOAP_Base * Generates the correct headers for the cookies. * * @access private + * + * @param array $options Cookie options. If 'nocookies' is set and true + * the cookies from the last response are added + * automatically. 'cookies' is name-value-hash with + * a list of cookies to add. + * + * @return string The cookie header value. */ - function _genCookieHeader() + function _generateCookieHeader($options) { - foreach ($this->cookies as $name=>$value) { - $cookies = (isset($cookies) ? $cookies. '; ' : '') . - urlencode($name) . '=' . urlencode($value); + $this->cookies = array(); + + if (empty($options['nocookies']) && + isset($this->result_cookies)) { + // Add the cookies we got from the last request. + foreach ($this->result_cookies as $cookie) { + if ($cookie['domain'] == $this->urlparts['host']) { + $this->cookies[$cookie['name']] = $cookie['value']; + } + } } + + // Add cookies the user wants to set. + if (isset($options['cookies'])) { + foreach ($options['cookies'] as $cookie) { + if ($cookie['domain'] == $this->urlparts['host']) { + $this->cookies[$cookie['name']] = $cookie['value']; + } + } + } + + $cookies = ''; + foreach ($this->cookies as $name => $value) { + if (!empty($cookies)) { + $cookies .= '; '; + } + $cookies .= urlencode($name) . '=' . urlencode($value); + } + return $cookies; } @@ -333,90 +326,95 @@ class SOAP_Transport_HTTP extends SOAP_Base */ function _parseResponse() { - if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", + if (!preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $this->incoming_payload, $match)) { - $this->response = $match[2]; - // Find the response error, some servers response with 500 for - // SOAP faults. - $this->_parseHeaders($match[1]); - - list($protocol, $code, $msg) = sscanf($this->result_headers[0], - '%s %s %s'); - unset($this->result_headers[0]); - - switch($code) { - case 100: // Continue - $this->incoming_payload = $match[2]; - return $this->_parseResponse(); - case 400: - $this->_raiseSoapFault("HTTP Response $code Bad Request"); - return false; - break; - case 401: - $this->_raiseSoapFault("HTTP Response $code Authentication Failed"); - return false; - break; - case 403: - $this->_raiseSoapFault("HTTP Response $code Forbidden"); - return false; - break; - case 404: - $this->_raiseSoapFault("HTTP Response $code Not Found"); - return false; - break; - case 407: - $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required"); - return false; - break; - case 408: - $this->_raiseSoapFault("HTTP Response $code Request Timeout"); - return false; - break; - case 410: - $this->_raiseSoapFault("HTTP Response $code Gone"); - return false; - break; - default: - if ($code >= 400 && $code < 500) { - $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg"); - return false; - } - } - - $this->_parseEncoding($match[1]); + $this->_raiseSoapFault('Invalid HTTP Response'); + return false; + } - if ($this->result_content_type == 'application/dime') { - // XXX quick hack insertion of DIME - if (PEAR::isError($this->_decodeDIMEMessage($this->response,$this->headers,$this->attachments))) { - // _decodeDIMEMessage already raised $this->fault - return false; + $this->response = $match[2]; + // Find the response error, some servers response with 500 for + // SOAP faults. + $this->_parseHeaders($match[1]); + + list(, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s'); + unset($this->result_headers[0]); + + switch($code) { + case 100: // Continue + $this->incoming_payload = $match[2]; + return $this->_parseResponse(); + case 200: + case 202: + $this->incoming_payload = trim($match[2]); + if (!strlen($this->incoming_payload)) { + /* Valid one-way message response. */ + return true; } - $this->result_content_type = $this->headers['content-type']; - } elseif (stristr($this->result_content_type,'multipart/related')) { - $this->response = $this->incoming_payload; - if (PEAR::isError($this->_decodeMimeMessage($this->response,$this->headers,$this->attachments))) { - // _decodeMimeMessage already raised $this->fault + break; + case 400: + $this->_raiseSoapFault("HTTP Response $code Bad Request"); + return false; + case 401: + $this->_raiseSoapFault("HTTP Response $code Authentication Failed"); + return false; + case 403: + $this->_raiseSoapFault("HTTP Response $code Forbidden"); + return false; + case 404: + $this->_raiseSoapFault("HTTP Response $code Not Found"); + return false; + case 407: + $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required"); + return false; + case 408: + $this->_raiseSoapFault("HTTP Response $code Request Timeout"); + return false; + case 410: + $this->_raiseSoapFault("HTTP Response $code Gone"); + return false; + default: + if ($code >= 400 && $code < 500) { + $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg"); return false; } - } elseif ($this->result_content_type != 'text/xml') { - $this->_raiseSoapFault($this->response); + break; + } + + $this->_parseEncoding($match[1]); + + if ($this->result_content_type == 'application/dime') { + // XXX quick hack insertion of DIME + if (PEAR::isError($this->_decodeDIMEMessage($this->response, $this->headers, $this->attachments))) { + // _decodeDIMEMessage already raised $this->fault return false; } - // if no content, return false - return strlen($this->response) > 0; + $this->result_content_type = $this->headers['content-type']; + } elseif (stristr($this->result_content_type, 'multipart/related')) { + $this->response = $this->incoming_payload; + if (PEAR::isError($this->_decodeMimeMessage($this->response, $this->headers, $this->attachments))) { + // _decodeMimeMessage already raised $this->fault + return false; + } + } elseif ($this->result_content_type != 'text/xml') { + $this->_raiseSoapFault($this->response); + return false; } - $this->_raiseSoapFault('Invalid HTTP Response'); - return false; + + // if no content, return false + return strlen($this->response) > 0; } /** - * Creates HTTP request, including headers, for outgoing request. + * Creates an HTTP request, including headers, for the outgoing request. + * + * @access private * * @param string $msg Outgoing SOAP package. * @param array $options Options. + * * @return string Outgoing payload. - * @access private */ function _getRequest($msg, $options) { @@ -451,30 +449,17 @@ class SOAP_Transport_HTTP extends SOAP_Base $this->headers['Content-Type'] = "text/xml; charset=$this->encoding"; $this->headers['Content-Length'] = strlen($msg); $this->headers['SOAPAction'] = '"' . $action . '"'; + $this->headers['Connection'] = 'close'; + if (isset($options['headers'])) { $this->headers = array_merge($this->headers, $options['headers']); } - $this->cookies = array(); - if (!isset($options['nocookies']) || !$options['nocookies']) { - // Add the cookies we got from the last request. - if (isset($this->result_cookies)) { - foreach ($this->result_cookies as $cookie) { - if ($cookie['domain'] == $this->urlparts['host']) - $this->cookies[$cookie['name']] = $cookie['value']; - } - } - } - // Add cookies the user wants to set. - if (isset($options['cookies'])) { - foreach ($options['cookies'] as $cookie) { - if ($cookie['domain'] == $this->urlparts['host']) - $this->cookies[$cookie['name']] = $cookie['value']; - } - } - if (count($this->cookies)) { - $this->headers['Cookie'] = $this->_genCookieHeader(); + $cookies = $this->_generateCookieHeader($options); + if ($cookies) { + $this->headers['Cookie'] = $cookies; } + $headers = ''; foreach ($this->headers as $k => $v) { $headers .= "$k: $v\r\n"; @@ -486,12 +471,14 @@ class SOAP_Transport_HTTP extends SOAP_Base } /** - * Sends outgoing request, and read/parse response. + * Sends the outgoing HTTP request and reads and parses the response. * - * @param string $msg Outgoing SOAP package. - * @param string $action SOAP Action. - * @return string Response data, minus HTTP headers. * @access private + * + * @param string $msg Outgoing SOAP package. + * @param array $options Options. + * + * @return string Response data without HTTP headers. */ function _sendHTTP($msg, $options) { @@ -541,18 +528,18 @@ class SOAP_Transport_HTTP extends SOAP_Base } /** - * Sends outgoing request, and read/parse response, via HTTPS. + * Sends the outgoing HTTPS request and reads and parses the response. * - * @param string $msg Outgoing SOAP package. - * @param string $action SOAP Action. - * @return string $response Response data, minus HTTP headers. * @access private + * + * @param string $msg Outgoing SOAP package. + * @param array $options Options. + * + * @return string Response data without HTTP headers. */ function _sendHTTPS($msg, $options) { - /* NOTE This function uses the CURL functions - * Your php must be compiled with CURL - */ + /* Check if the required curl extension is installed. */ if (!extension_loaded('curl')) { return $this->_raiseSoapFault('CURL Extension is required for HTTPS'); } @@ -560,41 +547,53 @@ class SOAP_Transport_HTTP extends SOAP_Base $ch = curl_init(); if (isset($options['proxy_host'])) { - // $options['http_proxy'] == 'hostname:port' - $host = $options['proxy_host']; $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080; - curl_setopt($ch, CURLOPT_PROXY, $host . ":" . $port); + curl_setopt($ch, CURLOPT_PROXY, + $options['proxy_host'] . ':' . $port); } - if (isset($options['proxy_user'])) { - // $options['http_proxy_userpw'] == 'username:password' - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $options['proxy_user'] . ':' . $options['proxy_pass']); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, + $options['proxy_user'] . ':' . $options['proxy_pass']); } if (isset($options['user'])) { - curl_setopt($ch, CURLOPT_USERPWD, $options['user'] . ':' . $options['pass']); + curl_setopt($ch, CURLOPT_USERPWD, + $options['user'] . ':' . $options['pass']); } - if (!isset($options['soapaction'])) { - $options['soapaction'] = ''; + $headers = array(); + $action = isset($options['soapaction']) ? $options['soapaction'] : ''; + $headers['Content-Type'] = "text/xml; charset=$this->encoding"; + $headers['SOAPAction'] = '"' . $action . '"'; + if (isset($options['headers'])) { + $headers = array_merge($headers, $options['headers']); } - curl_setopt($ch, CURLOPT_HTTPHEADER , array('Content-Type: text/xml;charset=' . $this->encoding, 'SOAPAction: "'.$options['soapaction'].'"')); - curl_setopt($ch, CURLOPT_USERAGENT , $this->_userAgent); + foreach ($headers as $header => $value) { + $headers[$header] = $header . ': ' . $value; + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_USERAGENT, $this->_userAgent); if ($this->timeout) { - curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); //times out after 4s + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); } - curl_setopt($ch, CURLOPT_POSTFIELDS, $msg); - curl_setopt($ch, CURLOPT_URL, $this->url); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_FAILONERROR, 0); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $msg); + curl_setopt($ch, CURLOPT_URL, $this->url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_FAILONERROR, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 1); if (defined('CURLOPT_HTTP_VERSION')) { curl_setopt($ch, CURLOPT_HTTP_VERSION, 1); } + if (!ini_get('safe_mode') && !ini_get('open_basedir')) { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + } + $cookies = $this->_generateCookieHeader($options); + if ($cookies) { + curl_setopt($ch, CURLOPT_COOKIE, $cookies); + } if (isset($options['curl'])) { foreach ($options['curl'] as $key => $val) { diff --git a/thirdparty/pear/SOAP/Transport/SMTP.php b/thirdparty/pear/SOAP/Transport/SMTP.php old mode 100644 new mode 100755 index 64dd045..5157317 --- a/thirdparty/pear/SOAP/Transport/SMTP.php +++ b/thirdparty/pear/SOAP/Transport/SMTP.php @@ -21,66 +21,64 @@ * @category Web Services * @package SOAP * @author Shane Caraveo - * @copyright 2003-2005 The PHP Group + * @author Jan Schneider + * @copyright 2003-2006 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ -require_once 'SOAP/Base.php'; +require_once 'SOAP/Transport.php'; require_once 'Mail/smtp.php'; /** - * SMTP Transport for SOAP + * SMTP Transport for SOAP * - * implements SOAP-SMTP as defined at + * Implements SOAP-SMTP as defined at * http://www.pocketsoap.com/specs/smtpbinding/ * - * TODO: use PEAR smtp and Mime classes + * @todo use PEAR smtp and Mime classes * * @access public * @package SOAP * @author Shane Caraveo + * @author Jan Schneider */ -class SOAP_Transport_SMTP extends SOAP_Base +class SOAP_Transport_SMTP extends SOAP_Transport { - var $credentials = ''; var $timeout = 4; // connect timeout - var $urlparts = NULL; - var $url = ''; - var $incoming_payload = ''; - var $_userAgent = SOAP_LIBRARY_NAME; - var $encoding = SOAP_DEFAULT_ENCODING; var $host = '127.0.0.1'; var $port = 25; - var $auth = NULL; + var $auth = null; + /** - * SOAP_Transport_SMTP Constructor - * - * @param string $URL mailto:address - * - * @access public - */ - function SOAP_Transport_SMTP($URL, $encoding='US-ASCII') + * SOAP_Transport_SMTP Constructor + * + * @param string $url mailto: address. + * + * @access public + */ + function SOAP_Transport_SMTP($url, $encoding = 'US-ASCII') { parent::SOAP_Base('SMTP'); $this->encoding = $encoding; - $this->urlparts = @parse_url($URL); - $this->url = $URL; + $this->urlparts = @parse_url($url); + $this->url = $url; } /** * Sends and receives SOAP data. * - * @param string $msg Outgoing POST data. - * @param string $action SOAP Action header data. - * @param integer $timeout Socket timeout, defaults to 0 or off. - * - * @return string Response data, minus HTTP headers. * @access public + * + * @param string Outgoing SOAP data. + * @param array Options. + * + * @return string|SOAP_Fault */ function send($msg, $options = array()) { + $this->fault = null; $this->incoming_payload = ''; $this->outgoing_payload = $msg; if (!$this->_validateUrl()) { @@ -148,22 +146,21 @@ class SOAP_Transport_SMTP extends SOAP_Base 'password' => $this->password, 'auth' => $this->auth ); - $mailer =& new Mail_smtp($mailer_params); + $mailer = new Mail_smtp($mailer_params); $result = $mailer->send($this->urlparts['path'], $headers, $out); if (!PEAR::isError($result)) { - $val =& new SOAP_Value('Message-ID', 'string', $headers['Message-ID']); + $val = new SOAP_Value('Message-ID', 'string', $headers['Message-ID']); } else { - $sval[] =& new SOAP_Value('faultcode', 'QName', 'SOAP-ENV:Client'); - $sval[] =& new SOAP_Value('faultstring', 'string', "couldn't send SMTP message to {$this->urlparts['path']}"); - $val =& new SOAP_Value('Fault', 'Struct', $sval); + $sval[] = new SOAP_Value('faultcode', 'QName', SOAP_BASE::SOAPENVPrefix().':Client'); + $sval[] = new SOAP_Value('faultstring', 'string', "couldn't send SMTP message to {$this->urlparts['path']}"); + $val = new SOAP_Value('Fault', 'Struct', $sval); } - $mqname =& new QName($method, $namespace); - $methodValue =& new SOAP_Value('Response', 'Struct', array($val)); + $methodValue = new SOAP_Value('Response', 'Struct', array($val)); - $this->incoming_payload = $this->_makeEnvelope($methodValue, - $this->headers, - $this->encoding); + $this->incoming_payload = $this->makeEnvelope($methodValue, + $this->headers, + $this->encoding); return $this->incoming_payload; } @@ -191,16 +188,16 @@ class SOAP_Transport_SMTP extends SOAP_Base function _validateUrl() { if (!is_array($this->urlparts)) { - $this->_raiseSoapFault("Unable to parse URL $url"); + $this->_raiseSoapFault("Unable to parse URL $this->url"); return false; } if (!isset($this->urlparts['scheme']) || strcasecmp($this->urlparts['scheme'], 'mailto') != 0) { - $this->_raiseSoapFault("Unable to parse URL $url"); + $this->_raiseSoapFault("Unable to parse URL $this->url"); return false; } if (!isset($this->urlparts['path'])) { - $this->_raiseSoapFault("Unable to parse URL $url"); + $this->_raiseSoapFault("Unable to parse URL $this->url"); return false; } return true; diff --git a/thirdparty/pear/SOAP/Transport/TCP.php b/thirdparty/pear/SOAP/Transport/TCP.php old mode 100644 new mode 100755 index a4813eb..cc386c7 --- a/thirdparty/pear/SOAP/Transport/TCP.php +++ b/thirdparty/pear/SOAP/Transport/TCP.php @@ -14,12 +14,13 @@ * @category Web Services * @package SOAP * @author Shane Hanna - * @copyright 2003-2005 The PHP Group + * @author Jan Schneider + * @copyright 2003-2006 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ -require_once 'SOAP/Base.php'; +require_once 'SOAP/Transport.php'; /** * TCP transport for SOAP. @@ -29,21 +30,12 @@ require_once 'SOAP/Base.php'; * @access public * @package SOAP * @author Shane Hanna + * @author Jan Schneider */ -class SOAP_Transport_TCP extends SOAP_Base_Object +class SOAP_Transport_TCP extends SOAP_Transport { - - var $headers = array(); - var $urlparts = null; - var $url = ''; - var $incoming_payload = ''; - var $_userAgent = SOAP_LIBRARY_NAME; - var $encoding = SOAP_DEFAULT_ENCODING; - var $result_encoding = 'UTF-8'; - var $result_content_type; - /** - * socket + * Socket. */ var $socket = null; @@ -85,14 +77,16 @@ class SOAP_Transport_TCP extends SOAP_Base_Object /** * Sends and receives SOAP data. * - * @param string $msg Outgoing POST data. - * @param string $action SOAP Action header data. + * @access public + * + * @param string Outgoing SOAP data. + * @param array Options. * * @return string|SOAP_Fault - * @access public */ - function send($msg, $options = NULL) + function send($msg, $options = array()) { + $this->fault = null; $this->incoming_payload = ''; $this->outgoing_payload = $msg; if (!$this->_validateUrl()) { @@ -142,11 +136,11 @@ class SOAP_Transport_TCP extends SOAP_Base_Object function _validateUrl() { if (!is_array($this->urlparts) ) { - $this->_raiseSoapFault("Unable to parse URL $url"); + $this->_raiseSoapFault("Unable to parse URL $this->url"); return false; } if (!isset($this->urlparts['host'])) { - $this->_raiseSoapFault("No host in URL $url"); + $this->_raiseSoapFault("No host in URL $this->url"); return false; } if (!isset($this->urlparts['path']) || !$this->urlparts['path']) { diff --git a/thirdparty/pear/SOAP/Transport/TEST.php b/thirdparty/pear/SOAP/Transport/TEST.php new file mode 100644 index 0000000..fabeba5 --- /dev/null +++ b/thirdparty/pear/SOAP/Transport/TEST.php @@ -0,0 +1,54 @@ + + * @copyright 2008 The PHP Group + * @license http://www.php.net/license/2_02.txt PHP License 2.02 + * @link http://pear.php.net/package/SOAP + */ + +require_once 'SOAP/Transport.php'; + +/** + * Test transport for SOAP. + * + * @access public + * @package SOAP + * @author Jan Schneider + */ +class SOAP_Transport_TEST extends SOAP_Transport +{ + /** + * Sends and receives SOAP data. + * + * @param string $msg Outgoing SOAP data. + * @param array $options Options. + * + * @return string|SOAP_Fault + */ + function send($msg, $options = array()) + { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->outgoing_payload = $msg; + ob_start(); + $server = clone($options['server']); + $server->service($msg); + $this->incoming_payload = ob_get_contents(); + ob_end_clean(); + return $this->incoming_payload; + } + +} diff --git a/thirdparty/pear/SOAP/Type/dateTime.php b/thirdparty/pear/SOAP/Type/dateTime.php old mode 100644 new mode 100755 index 942cd02..4fcec72 --- a/thirdparty/pear/SOAP/Type/dateTime.php +++ b/thirdparty/pear/SOAP/Type/dateTime.php @@ -1,6 +1,6 @@ Original Author * @author Shane Caraveo Port to PEAR and more * @author Jan Schneider Maintenance - * @copyright 2003-2005 The PHP Group + * @copyright 2003-2007 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ @@ -30,7 +30,7 @@ * @author Shane Caraveo Port to PEAR and more * @author Jan Schneider Maintenance */ -class SOAP_Type_dateTime +class SOAP_Type_dateTime { var $_iso8601 = '# 1: centuries & years CCYY- @@ -94,7 +94,12 @@ class SOAP_Type_dateTime return 0; } - return date('Y-m-d\TH:i:sO', $timestamp); + //simulate PHP5's P parameter + $zone = date('O', $timestamp); + if (strlen($zone) === 5) { + $zone = substr($zone, 0, 3) . ':' . substr($zone, 3); + } + return date('Y-m-d\TH:i:s', $timestamp) . $zone; } /** diff --git a/thirdparty/pear/SOAP/Type/duration.php b/thirdparty/pear/SOAP/Type/duration.php old mode 100644 new mode 100755 index 078e4a4..f7d7925 --- a/thirdparty/pear/SOAP/Type/duration.php +++ b/thirdparty/pear/SOAP/Type/duration.php @@ -1,78 +1,103 @@ Port to PEAR and more + * @author Jan Schneider Maintenance + * @copyright 2003-2007 The PHP Group + * @license http://www.php.net/license/2_02.txt PHP License 2.02 + * @link http://pear.php.net/package/SOAP + */ + +/** + * This is only an aproximation of duration, more work still to do. See the + * schema url for more info on duration. + * + * http://www.w3.org/TR/xmlschema-2/ + * + * [Definition:] duration represents a duration of time. The value space of + * duration is a six-dimensional space where the coordinates designate the + * Gregorian year, month, day, hour, minute, and second components + * defined in 5.5.3.2 of [ISO 8601], respectively. These components are + * ordered in their significance by their order of appearance i.e. as year, + * month, day, hour, minute, and second. + * + * 3.2.6.1 Lexical representation + * The lexical representation for duration is the [ISO 8601] extended + * format PnYn MnDTnH nMnS, where nY represents the number of + * years, nM the number of months, nD the number of days, 'T' is the + * date/time separator, nH the number of hours, nM the number of + * minutes and nS the number of seconds. The number of seconds + * can include decimal digits to arbitrary precision. + * + * The values of the Year, Month, Day, Hour and Minutes components + * are not restricted but allow an arbitrary integer. Similarly, the + * value of the Seconds component allows an arbitrary decimal. + * Thus, the lexical representation of duration does not follow the + * alternative format of 5.5.3.2.1 of [ISO 8601]. + * + * An optional preceding minus sign ('-') is allowed, to indicate a + * negative duration. If the sign is omitted a positive duration is + * indicated. See also ISO 8601 Date and Time Formats (D). + * + * For example, to indicate a duration of 1 year, 2 months, 3 days, + * 10 hours, and 30 minutes, one would write: P1Y2M3DT10H30M. + * One could also indicate a duration of minus 120 days as: -P120D. + * + * Reduced precision and truncated representations of this format + * are allowed provided they conform to the following: + * + * If the number of years, months, days, hours, minutes, or seconds + * in any expression equals zero, the number and its corresponding + * designator *may* be omitted. However, at least one number and + * its designator *must* be present. + * The seconds part *may* have a decimal fraction. + * The designator 'T' shall be absent if all of the time items are absent. + * The designator 'P' must always be present. + * For example, P1347Y, P1347M and P1Y2MT2H are all allowed; P0Y1347M + * and P0Y1347M0D are allowed. P-1347M is not allowed although -P1347M + * is allowed. P1Y2MT is not allowed. + * + * @access public + * @package SOAP + * @author Shane Caraveo Port to PEAR and more + * @author Jan Schneider Maintenance + * @todo Figure out best aproximation for year and month conversion to + * seconds + */ class SOAP_Type_duration { // format PnYnMnDTnHnMnS - function unix_to_duration($seconds) { + function unix_to_duration($seconds) + { return SOAP_Type_duration::getduration($seconds); } - - function mod($a, $b, &$d, &$r) { - $d = floor( $a / $b ); + + function mod($a, $b, &$d, &$r) + { + $d = floor($a / $b); $r = $a % $b; } - - function getduration($seconds) { + + function getduration($seconds) + { $neg = ''; if ($seconds < 0) { $neg = '-'; $seconds = $seconds * -1; } - + $_mi = 60; $_h = $_mi * 60; $_d = $_h * 24; @@ -85,7 +110,7 @@ class SOAP_Type_duration SOAP_Type_duration::mod($seconds, $_d, $d, $seconds); SOAP_Type_duration::mod($seconds, $_h, $h, $seconds); SOAP_Type_duration::mod($seconds, $_mi, $mi, $s); - + $duration = $neg.'P'; if ($y) $duration .= $y.'Y'; if ($m) $duration .= $m.'M'; @@ -95,36 +120,40 @@ class SOAP_Type_duration if ($mi) $duration .= $mi.'M'; if ($s) $duration .= $s.'S'; if ($duration == 'P' || $duration == '-P') $duration = 'PT0S'; + return $duration; } - - function mkduration($n, $Y, $Mo, $D, $H, $Mi, $S) { + + function mkduration($n, $Y, $Mo, $D, $H, $Mi, $S) + { $_mi = 60; $_h = $_mi * 60; $_d = $_h * 24; // XXX how do we properly handle month and year values? $_m = $_d * 30; $_y = $_d * 365; - + $sec = $Y * $_y + $Mo * $_m + $D * $_d + $H * $_h + $Mi * $_mi + $S; if ($n == '-') $sec = $sec * -1; + return $sec; } - - function duration_to_unix($duration) { - global $ereg_duration; - if (ereg($ereg_duration,$duration,$regs)) { + + function duration_to_unix($duration) + { + if (ereg('(-)?P([0-9]+Y)?([0-9]+M)?([0-9]+D)?T?([0-9]+H)?([0-9]+M)?([0-9]+S)?', $duration, $regs)) { return SOAP_Type_duration::mkduration($regs[1], $regs[2], $regs[3], $regs[4], $regs[5], $regs[6], $regs[7]); } - return FALSE; + return false; } - - function is_duration($duration) { - global $ereg_duration; - return ereg($ereg_duration,$duration,$regs); + + function is_duration($duration) + { + return ereg('(-)?P([0-9]+Y)?([0-9]+M)?([0-9]+D)?T?([0-9]+H)?([0-9]+M)?([0-9]+S)?', $duration, $regs); } - - function _test($time) { + + function _test($time) + { if (SOAP_Type_duration::is_duration($time)) { $t = SOAP_Type_duration::duration_to_unix($time); echo "Duration: $time is ".$t." seconds\n"; @@ -134,14 +163,16 @@ class SOAP_Type_duration } return $t; } - - function add($d1, $d2) { + + function add($d1, $d2) + { $s1 = SOAP_Type_duration::duration_to_unix($d1); $s2 = SOAP_Type_duration::duration_to_unix($d2); return SOAP_Type_duration::unix_to_duration($s1 + $s2); } - - function subtract($d1, $d2) { + + function subtract($d1, $d2) + { $s1 = SOAP_Type_duration::duration_to_unix($d1); $s2 = SOAP_Type_duration::duration_to_unix($d2); return SOAP_Type_duration::unix_to_duration($s1 - $s2); @@ -149,17 +180,15 @@ class SOAP_Type_duration } -/* tests */ - +/* Tests. */ $t = SOAP_Type_duration::_test('P1Y2M3DT10H30M'); SOAP_Type_duration::_test($t); $t = SOAP_Type_duration::_test('-P120D'); SOAP_Type_duration::_test($t); -// duration since 1970 +/* Duration since 1970. */ $t = SOAP_Type_duration::_test(time()); SOAP_Type_duration::_test($t); -print "Add should be PT0S: ".SOAP_Type_duration::add('-P120D','P4M')."\n"; -print "Subtract should be PT0S: ".SOAP_Type_duration::subtract('P120D','P4M')."\n"; -?> \ No newline at end of file +echo 'Add should be PT0S: ' . SOAP_Type_duration::add('-P120D','P4M') . "\n"; +echo 'Subtract should be PT0S: ' . SOAP_Type_duration::subtract('P120D','P4M') . "\n"; diff --git a/thirdparty/pear/SOAP/Type/hexBinary.php b/thirdparty/pear/SOAP/Type/hexBinary.php old mode 100644 new mode 100755 index 6acdc21..e6bbdf4 --- a/thirdparty/pear/SOAP/Type/hexBinary.php +++ b/thirdparty/pear/SOAP/Type/hexBinary.php @@ -1,46 +1,45 @@ Port to PEAR and more | -// | Authors: Dietrich Ayala Original Author | -// +----------------------------------------------------------------------+ -// -// $Id$ -// -class SOAP_Type_hexBinary -{ +/** + * This class provides methods to detect and convert binary data from an to + * hexadecimal strings. + * + * PHP versions 4 and 5 + * + * LICENSE: This source file is subject to version 2.02 of the PHP license, + * that is bundled with this package in the file LICENSE, and is available at + * through the world-wide-web at http://www.php.net/license/2_02.txt. If you + * did not receive a copy of the PHP license and are unable to obtain it + * through the world-wide-web, please send a note to license@php.net so we can + * mail you a copy immediately. + * + * @category Web Services + * @package SOAP + * @author Dietrich Ayala Original Author + * @author Shane Caraveo Port to PEAR and more + * @copyright 2003-2007 The PHP Group + * @license http://www.php.net/license/2_02.txt PHP License 2.02 + * @link http://pear.php.net/package/SOAP + */ +class SOAP_Type_hexBinary { + function to_bin($value) { - $len = strlen($value); - return pack('H' . $len, $value); + return pack('H' . strlen($value), $value); } + function to_hex($value) { return bin2hex($value); } + function is_hexbin($value) { - # first see if there are any invalid chars - $l = strlen($value); + // First see if there are any invalid chars. + if (!strlen($value) || preg_match('/[^A-Fa-f0-9]/', $value)) { + return false; + } - if ($l < 1 || strspn($value, '0123456789ABCDEFabcdef') != $l) return FALSE; - - $bin = SOAP_Type_hexBinary::to_bin($value); - $hex = SOAP_Type_hexBinary::to_hex($bin); - return strcasecmp($value, $hex) == 0; + return strcasecmp($value, SOAP_Type_hexBinary::to_hex(SOAP_Type_hexBinary::to_bin($value))) == 0; } -} -?> \ No newline at end of file +} diff --git a/thirdparty/pear/SOAP/Value.php b/thirdparty/pear/SOAP/Value.php old mode 100644 new mode 100755 index 5bf468b..30c0ce6 --- a/thirdparty/pear/SOAP/Value.php +++ b/thirdparty/pear/SOAP/Value.php @@ -17,7 +17,7 @@ * @author Shane Caraveo Port to PEAR and more * @author Chuck Hagenbuch Maintenance * @author Jan Schneider Maintenance - * @copyright 2003-2005 The PHP Group + * @copyright 2003-2007 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */ @@ -40,39 +40,76 @@ require_once 'SOAP/Base.php'; class SOAP_Value { /** - * @var string + * The actual value. + * + * @var mixed */ var $value = null; /** + * QName instance representing the value name. + * + * @var QName + */ + var $nqn; + + /** + * The value name, without namespace information. + * * @var string */ var $name = ''; /** + * The namespace of the value name. + * + * @var string + */ + var $namespace = ''; + + /** + * QName instance representing the value type. + * + * @var QName + */ + var $tqn; + + /** + * The value type, without namespace information. + * * @var string */ var $type = ''; /** - * Namespace + * The namespace of the value type. * * @var string */ - var $namespace = ''; var $type_namespace = ''; - var $attributes = array(); - /** + * The type of the array elements, if this value is an array. + * * @var string */ var $arrayType = ''; - var $options = array(); + /** + * A hash of additional attributes. + * + * @see SOAP_Value() + * @var array + */ + var $attributes = array(); - var $nqn; - var $tqn; + /** + * List of encoding and serialization options. + * + * @see SOAP_Value() + * @var array + */ + var $options = array(); /** * Constructor. @@ -81,21 +118,34 @@ class SOAP_Value * @param mixed $type SOAP value {namespace}type. Determined * automatically if not set. * @param mixed $value Value to set. - * @param array $attributes Attributes. + * @param array $attributes A has of additional XML attributes to be + * added to the serialized value. + * @param array $options A list of encoding and serialization options: + * - 'attachment': array with information about + * the attachment + * - 'soap_encoding': defines encoding for SOAP + * message part of a MIME encoded SOAP request + * (default: base64) + * - 'keep_arrays_flat': use the tag name + * multiple times for each element when + * passing in an array in literal mode + * - 'no_type_prefix': supress adding of the + * namespace prefix */ function SOAP_Value($name = '', $type = false, $value = null, - $attributes = array()) + $attributes = array(), $options = array()) { - // Detect type if not passed. - $this->nqn =& new QName($name); + $this->nqn = new QName($name); $this->name = $this->nqn->name; $this->namespace = $this->nqn->namespace; - $this->tqn =& new QName($type); - $this->type = $this->tqn->name; - $this->type_prefix = $this->tqn->ns; - $this->type_namespace = $this->tqn->namespace; - $this->value =& $value; + if ($type) { + $this->tqn = new QName($type); + $this->type = $this->tqn->name; + $this->type_namespace = $this->tqn->namespace; + } + $this->value = $value; $this->attributes = $attributes; + $this->options = $options; } /** @@ -109,10 +159,8 @@ class SOAP_Value function serialize(&$serializer) { return $serializer->_serializeValue($this->value, - $this->name, - $this->type, - $this->namespace, - $this->type_namespace, + $this->nqn, + $this->tqn, $this->options, $this->attributes, $this->arrayType); @@ -156,11 +204,11 @@ class SOAP_Header extends SOAP_Value parent::SOAP_Value($name, $type, $value, $attributes); if (isset($actor)) { - $this->attributes['SOAP-ENV:actor'] = $actor; - } elseif (!isset($this->attributes['SOAP-ENV:actor'])) { - $this->attributes['SOAP-ENV:actor'] = 'http://schemas.xmlsoap.org/soap/actor/next'; + $this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'] = $actor; + } elseif (!isset($this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'])) { + $this->attributes[SOAP_BASE::SOAPENVPrefix().':actor'] = 'http://schemas.xmlsoap.org/soap/actor/next'; } - $this->attributes['SOAP-ENV:mustUnderstand'] = (int)$mustunderstand; + $this->attributes[SOAP_BASE::SOAPENVPrefix().':mustUnderstand'] = (int)$mustunderstand; } } @@ -183,18 +231,14 @@ class SOAP_Attachment extends SOAP_Value * @param string $filename The attachment's file name. Ignored if $file * is provide. * @param string $file The attachment data. + * @param array $attributes Attributes. */ function SOAP_Attachment($name = '', $type = 'application/octet-stream', - $filename, $file = null) + $filename, $file = null, $attributes = null) { parent::SOAP_Value($name, null, null); - if (!isset($GLOBALS['SOAP_options']['Mime'])) { - $this->options['attachment'] = PEAR::raiseError('Mail_mime is not installed, unable to support SOAP Attachements'); - return; - } - - $filedata = ($file === null) ? $this->_file2str($filename) : $file; + $filedata = $file === null ? $this->_file2str($filename) : $file; $filename = basename($filename); if (PEAR::isError($filedata)) { $this->options['attachment'] = $filedata; @@ -203,7 +247,8 @@ class SOAP_Attachment extends SOAP_Value $cid = md5(uniqid(time())); - $this->attributes['href'] = 'cid:' . $cid; + $this->attributes = $attributes; + $this->attributes['href'] = 'cid:' . $cid; $this->options['attachment'] = array('body' => $filedata, 'disposition' => $filename, diff --git a/thirdparty/pear/SOAP/WSDL.php b/thirdparty/pear/SOAP/WSDL.php old mode 100644 new mode 100755 index db8b907..c7ce5b5 --- a/thirdparty/pear/SOAP/WSDL.php +++ b/thirdparty/pear/SOAP/WSDL.php @@ -27,7 +27,6 @@ require_once 'SOAP/Fault.php'; require_once 'HTTP/Request.php'; define('WSDL_CACHE_MAX_AGE', 43200); -define('WSDL_CACHE_USE', 0); // set to zero to turn off caching /** * This class parses WSDL files, and can be used by SOAP::Client to properly @@ -37,7 +36,6 @@ define('WSDL_CACHE_USE', 0); // set to zero to turn off caching * http://dietrich.ganx4.com/soapx4 * * @todo - * - add wsdl caching * - refactor namespace handling ($namespace/$ns) * - implement IDL type syntax declaration so we can generate WSDL * @@ -61,31 +59,55 @@ class SOAP_WSDL extends SOAP_Base var $imports = array(); var $services = array(); var $service = ''; - var $uri = ''; - var $docs = false; /** - * Proxy parameters + * URL to WSDL file. + * + * @var string + */ + var $uri; + + /** + * Parse documentation in the WSDL? + * + * @var boolean + */ + var $docs; + + /** + * Proxy parameters. * * @var array */ - var $proxy = null; + var $proxy; - var $trace = 0; + /** + * Enable tracing in the generated proxy class? + * + * @var boolean + */ + var $trace = false; /** - * Use WSDL cache. + * Use WSDL cache? * * @var boolean */ - var $cacheUse = null; + var $cacheUse; /** - * Cache max lifetime (in seconds). + * WSDL cache directory. + * + * @var string + */ + var $cacheDir; + + /** + * Cache maximum lifetime (in seconds). * * @var integer */ - var $cacheMaxAge = null; + var $cacheMaxAge; /** * Class to use for WSDL parsing. Can be overridden for special cases, @@ -96,30 +118,63 @@ class SOAP_WSDL extends SOAP_Base var $wsdlParserClass = 'SOAP_WSDL_Parser'; /** + * Reserved PHP keywords. + * + * @link http://www.php.net/manual/en/reserved.php + * + * @var array + */ + var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case', + 'catch', 'cfunction', 'class', 'clone', 'const', + 'continue', 'declare', 'default', 'die', 'do', + 'echo', 'else', 'elseif', 'empty', 'enddeclare', + 'endfor', 'endforeach', 'endif', 'endswitch', + 'endwhile', 'eval', 'exception', 'exit', 'extends', + 'final', 'for', 'foreach', 'function', 'global', + 'if', 'implements', 'include', 'include_once', + 'interface', 'isset', 'list', 'new', 'old_function', + 'or', 'php_user_filter', 'print', 'private', + 'protected', 'public', 'require', 'require_once', + 'return', 'static', 'switch', 'this', 'throw', + 'try', 'unset', 'use', 'var', 'while', 'xor'); + + /** + * Regular expressions for invalid PHP labels. + * + * @link http://www.php.net/manual/en/language.variables.php. + * + * @var string + */ + var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/'); + + /** * SOAP_WSDL constructor. * - * @param string $wsdl_uri URL to WSDL file. - * @param array $proxy Contains options for HTTP_Request class - * @see HTTP_Request. - * @param boolean $cacheUse Use WSDL caching. Defaults to false. - * @param integer $cacheMaxAge Cache max lifetime (in seconds). - * @param boolean $docs Parse documentation in the WSDL? Defaults - * to false. + * @param string $wsdl_uri URL to WSDL file. + * @param array $proxy Options for HTTP_Request class + * @see HTTP_Request. + * @param boolean|string $cacheUse Use WSDL caching? The cache directory + * if a string. + * @param integer $cacheMaxAge Cache maximum lifetime (in seconds). + * @param boolean $docs Parse documentation in the WSDL? * * @access public */ function SOAP_WSDL($wsdl_uri = false, $proxy = array(), - $cacheUse = WSDL_CACHE_USE, + $cacheUse = false, $cacheMaxAge = WSDL_CACHE_MAX_AGE, $docs = false) { parent::SOAP_Base('WSDL'); $this->uri = $wsdl_uri; $this->proxy = $proxy; - $this->cacheUse = $cacheUse; + $this->cacheUse = !empty($cacheUse); $this->cacheMaxAge = $cacheMaxAge; $this->docs = $docs; + if (is_string($cacheUse)) { + $this->cacheDir = $cacheUse; + } if ($wsdl_uri) { if (!PEAR::isError($this->parseURL($wsdl_uri))) { @@ -129,29 +184,32 @@ class SOAP_WSDL extends SOAP_Base } } + /** + * @deprecated Use setService(). + */ function set_service($service) { - if (array_key_exists($service, $this->services)) { - $this->service = $service; - } + $this->setService($service); } /** - * @deprecated use parseURL instead + * Sets the service currently to be used. + * + * @param string $service An (existing) service name. */ - function parse($wsdl_uri, $proxy = array()) + function setService($service) { - $this->parseURL($wsdl_uri, $proxy); + if (array_key_exists($service, $this->services)) { + $this->service = $service; + } } /** * Fills the WSDL array tree with data from a WSDL file. * * @param string $wsdl_uri URL to WSDL file. - * @param array $proxy Contains options for HTTP_Request class - * @see HTTP_Request. */ - function parseURL($wsdl_uri, $proxy = array()) + function parseURL($wsdl_uri) { $parser =& new $this->wsdlParserClass($wsdl_uri, $this, $this->docs); @@ -171,22 +229,22 @@ class SOAP_WSDL extends SOAP_Base * @param string $service_desc Optional description of the WSDL * service. */ - function parseObject(&$wsdl_obj, $targetNamespace, $service_name, + function parseObject($wsdl_obj, $targetNamespace, $service_name, $service_desc = '') { - $parser =& new SOAP_WSDL_ObjectParser($wsdl_obj, $this, - $targetNamespace, $service_name, - $service_desc); + $parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this, + $targetNamespace, $service_name, + $service_desc); - if ($parser->fault) { - $this->_raiseSoapFault($parser->fault); - } + if ($parser->fault) { + $this->_raiseSoapFault($parser->fault); + } } function getEndpoint($portName) { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } return (isset($this->services[$this->service]['ports'][$portName]['address']['location'])) @@ -215,8 +273,8 @@ class SOAP_WSDL extends SOAP_Base */ function getPortName($operation, $service = null) { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } if (!$service) { @@ -241,8 +299,8 @@ class SOAP_WSDL extends SOAP_Base function getOperationData($portName, $operation) { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } if (!isset($this->services[$this->service]['ports'][$portName]['binding']) || @@ -254,7 +312,7 @@ class SOAP_WSDL extends SOAP_Base if (is_array($this->bindings[$binding]['operations'][$operation])) { $opData = $this->bindings[$binding]['operations'][$operation]; } - // get operation data from porttype + // Get operation data from porttype. $portType = $this->bindings[$binding]['type']; if (!$portType) { return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri); @@ -314,12 +372,12 @@ class SOAP_WSDL extends SOAP_Base function matchMethod(&$operation) { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } // Overloading lowercases function names :( - foreach ($this->services[$this->service]['ports'] as $port => $portAttrs) { + foreach ($this->services[$this->service]['ports'] as $portAttrs) { foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) { if (strcasecmp($op, $operation) == 0) { $operation = $op; @@ -346,30 +404,33 @@ class SOAP_WSDL extends SOAP_Base $namespace = $this->namespaces[$namespace]; } - if (isset($this->ns[$namespace])) { - $nsp = $this->ns[$namespace]; - //if (!isset($this->elements[$nsp])) - // $nsp = $this->namespaces[$nsp]; - if (isset($this->elements[$nsp][$datatype])) { - $checkmessages = array(); - // Find what messages use this datatype. - foreach ($this->messages as $messagename => $message) { - foreach ($message as $partname => $part) { - if ($part['type'] == $datatype) { - $checkmessages[] = $messagename; - break; - } - } + if (!isset($this->ns[$namespace])) { + return null; + } + + $nsp = $this->ns[$namespace]; + //if (!isset($this->elements[$nsp])) + // $nsp = $this->namespaces[$nsp]; + if (!isset($this->elements[$nsp][$datatype])) { + return null; + } + + $checkmessages = array(); + // Find what messages use this datatype. + foreach ($this->messages as $messagename => $message) { + foreach ($message as $part) { + if ($part['type'] == $datatype) { + $checkmessages[] = $messagename; + break; } - // Find the operation that uses this message. - $dataHandler = null; - foreach($this->portTypes as $portname => $porttype) { - foreach ($porttype as $opname => $opinfo) { - foreach ($checkmessages as $messagename) { - if ($opinfo['input']['message'] == $messagename) { - return $opname; - } - } + } + } + // Find the operation that uses this message. + foreach($this->portTypes as $porttype) { + foreach ($porttype as $opname => $opinfo) { + foreach ($checkmessages as $messagename) { + if ($opinfo['input']['message'] == $messagename) { + return $opname; } } } @@ -380,8 +441,8 @@ class SOAP_WSDL extends SOAP_Base function getSoapAction($portName, $operation) { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) { @@ -393,8 +454,8 @@ class SOAP_WSDL extends SOAP_Base function getNamespace($portName, $operation) { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) { @@ -472,7 +533,7 @@ class SOAP_WSDL extends SOAP_Base $comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n"; } $comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n"; - $comments .= " \${$_argtype['type']} =& new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n"; + $comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n"; $this->_addArg($args, $argarray, $_argtype['type']); if (isset($el['type']) && isset($this->complexTypes[$tns][$el['type']]['attribute'])) { @@ -483,13 +544,13 @@ class SOAP_WSDL extends SOAP_Base } } elseif (isset($el['elements'])) { foreach ($el['elements'] as $ename => $element) { - $comments .= " \$$ename =& new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" . + $comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" . (isset($element['type']) ? $element['type'] : false) . "', \$$ename);\n"; $this->_addArg($args, $argarray, $ename); } } else { - $comments .= " \$$_argname =& new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n"; + $comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n"; $this->_addArg($args, $argarray, $_argname); } @@ -506,7 +567,7 @@ class SOAP_WSDL extends SOAP_Base $comments .= " // $_argname may require attributes, refer to wsdl for more info\n"; } $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type']; - $comments .= " \$$_argname =& new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n"; + $comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n"; } $this->_addArg($args, $argarray, $_argname); @@ -520,8 +581,8 @@ class SOAP_WSDL extends SOAP_Base */ function generateProxyCode($port = '', $classname = '') { - if ($this->__isfault()) { - return $this->__getfault(); + if ($this->_isfault()) { + return $this->_getfault(); } $multiport = count($this->services[$this->service]['ports']) > 1; @@ -542,7 +603,7 @@ class SOAP_WSDL extends SOAP_Base } else { $classname = 'WebService_' . $this->service; } - $classname = preg_replace('/[ .\-\(\)]+/', '_', $classname); + $classname = $this->_sanitize($classname); } if (!$this->_validateString($classname)) { @@ -611,12 +672,12 @@ class SOAP_WSDL extends SOAP_Base foreach ($operation['input'] as $argname => $argtype) { if ($argname == 'message') { foreach ($this->messages[$argtype] as $_argname => $_argtype) { + $_argname = $this->_sanitize($_argname); if ($opstyle == 'document' && $use == 'literal' && $_argtype['name'] == 'parameters') { // The type or element refered to is used for // parameters. $elattrs = null; - $element = $_argtype['element']; $el = $this->elements[$_argtype['namespace']][$_argtype['type']]; if ($el['complex']) { @@ -626,6 +687,7 @@ class SOAP_WSDL extends SOAP_Base } if (isset($el['elements'])) { foreach ($el['elements'] as $elname => $elattrs) { + $elname = $this->_sanitize($elname); // Is the element a complex type? if (isset($this->complexTypes[$elattrs['namespace']][$elname])) { $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname); @@ -636,7 +698,7 @@ class SOAP_WSDL extends SOAP_Base } if ($el['complex'] && $argarray) { $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name']; - $comments .= " \${$el['name']} =& new SOAP_Value('$wrapname', false, \$v = array($argarray));\n"; + $comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n"; $argarray = "'{$el['name']}' => \${$el['name']}"; } } else { @@ -658,7 +720,7 @@ class SOAP_WSDL extends SOAP_Base // legal. This could potentially cause collisions, but let's try // to make everything callable and see how many problems that // causes. - $opname_php = preg_replace('/[ .\-\(\)]+/', '_', $opname); + $opname_php = $this->_sanitize($opname); if (!$this->_validateString($opname_php)) { return null; } @@ -676,7 +738,7 @@ class SOAP_WSDL extends SOAP_Base " 'soapaction' => '$soapaction',\n" . " 'style' => '$opstyle',\n" . " 'use' => '$use'" . - ($this->trace?",\n 'trace' => 1" : '') . "));\n" . + ($this->trace ? ",\n 'trace' => true" : '') . "));\n" . " return \$result;\n" . " }\n"; } @@ -698,8 +760,8 @@ class SOAP_WSDL extends SOAP_Base function &getProxy($port = '', $name = '') { - if ($this->__isfault()) { - $fault =& $this->__getfault(); + if ($this->_isfault()) { + $fault =& $this->_getfault(); return $fault; } @@ -720,7 +782,7 @@ class SOAP_WSDL extends SOAP_Base $classname = $name . '_' . $classname; } - $classname = preg_replace('/[ .\-\(\)]+/', '_', $classname); + $classname = $this->_sanitize($classname); if (!class_exists($classname)) { $proxy = $this->generateProxyCode($port, $classname); require_once 'SOAP/Client.php'; @@ -731,6 +793,24 @@ class SOAP_WSDL extends SOAP_Base return $proxy; } + /** + * Sanitizes a SOAP value, method or class name so that it can be used as + * a valid PHP identifier. Invalid characters are converted into + * underscores and reserved words are prefixed with an underscore. + * + * @param string $name The identifier to sanitize. + * + * @return string The sanitized identifier. + */ + function _sanitize($name) + { + $name = preg_replace($this->_invalid, '_', $name); + if (in_array($name, $this->_reserved)) { + $name = '_' . $name; + } + return $name; + } + function &_getComplexTypeForElement($name, $namespace) { $t = null; @@ -758,75 +838,86 @@ class SOAP_WSDL extends SOAP_Base function getComplexTypeChildType($ns, $name, $child_ns, $child_name) { - // is the type an element? + // Is the type an element? $t = $this->_getComplexTypeForElement($name, $ns); if ($t) { - // no, get it from complex types directly + // No, get it from complex types directly. if (isset($t['elements'][$child_name]['type'])) return $t['elements'][$child_name]['type']; + } elseif (isset($this->ns[$ns]) && + isset($this->elements[$this->ns[$ns]][$name]['complex']) && + $this->elements[$this->ns[$ns]][$name]['complex']) { + // Type is not an element but complex. + return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type']; } return null; } - function getSchemaType($type, $name, $type_namespace) + /** + * @param QName $name A parameter name. + * @param QName $type A parameter type. + * + * @return array A list of [type, array element type, array element + * namespace, array length]. + */ + function getSchemaType($type, $name) { // see if it's a complex type so we can deal properly with // SOAPENC:arrayType. if ($name && $type) { // XXX TODO: // look up the name in the wsdl and validate the type. - foreach ($this->complexTypes as $ns => $types) { - if (array_key_exists($type, $types)) { - if (array_key_exists('type', $types[$type])) { - list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type]['arrayType'])? - $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']) - : array($this->namespaces[$types[$type]['namespace']], null, 0); - return array($types[$type]['type'], $arraytype, $arraytype_ns, $array_depth); + foreach ($this->complexTypes as $types) { + if (isset($types[$type->name])) { + if (isset($types[$type->name]['type'])) { + list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type->name]['arrayType']) + ? $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType']) + : array($this->namespaces[$types[$type->name]['namespace']], null, 0); + return array($types[$type->name]['type'], $arraytype, $arraytype_ns, $array_depth); } - if (array_key_exists('arrayType', $types[$type])) { + if (isset($types[$type->name]['arrayType'])) { list($arraytype_ns, $arraytype, $array_depth) = - $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']); + $this->_getDeepestArrayType($types[$type->name]['namespace'], $types[$type->name]['arrayType']); return array('Array', $arraytype, $arraytype_ns, $array_depth); } - if (array_key_exists('elements', $types[$type]) && - array_key_exists($name, $types[$type]['elements'])) { - $type = $types[$type]['elements']['type']; - return array($type, null, $this->namespaces[$types[$type]['namespace']], null); + if (!empty($types[$type->name]['elements'][$name->name])) { + $type->name = $types[$type->name]['elements']['type']; + return array($type->name, null, $this->namespaces[$types[$type->name]['namespace']], null); } + break; } } } - if ($type && $type_namespace) { + if ($type && $type->namespace) { $arrayType = null; // XXX TODO: - // this code currently handles only one way of encoding array types in wsdl - // need to do a generalized function to figure out complex types - $p = $this->ns[$type_namespace]; - if ($p && - array_key_exists($p, $this->complexTypes) && - array_key_exists($type, $this->complexTypes[$p])) { - if ($arrayType = $this->complexTypes[$p][$type]['arrayType']) { - $type = 'Array'; - } elseif ($this->complexTypes[$p][$type]['order']=='sequence' && - array_key_exists('elements', $this->complexTypes[$p][$type])) { - reset($this->complexTypes[$p][$type]['elements']); + // this code currently handles only one way of encoding array + // types in wsdl need to do a generalized function to figure out + // complex types + $p = $this->ns[$type->namespace]; + if ($p && !empty($this->complexTypes[$p][$type->name])) { + if ($arrayType = $this->complexTypes[$p][$type->name]['arrayType']) { + $type->name = 'Array'; + } elseif ($this->complexTypes[$p][$type->name]['order'] == 'sequence' && + array_key_exists('elements', $this->complexTypes[$p][$type->name])) { + reset($this->complexTypes[$p][$type->name]['elements']); // assume an array - if (count($this->complexTypes[$p][$type]['elements']) == 1) { - $arg = current($this->complexTypes[$p][$type]['elements']); + if (count($this->complexTypes[$p][$type->name]['elements']) == 1) { + $arg = current($this->complexTypes[$p][$type->name]['elements']); $arrayType = $arg['type']; - $type = 'Array'; + $type->name = 'Array'; } else { - foreach ($this->complexTypes[$p][$type]['elements'] as $element) { - if ($element['name'] == $type) { + foreach ($this->complexTypes[$p][$type->name]['elements'] as $element) { + if ($element['name'] == $type->name) { $arrayType = $element['type']; - $type = $element['type']; + $type->name = $element['type']; } } } } else { - $type = 'Struct'; + $type->name = 'Struct'; } - return array($type, $arrayType, $type_namespace, null); + return array($type->name, $arrayType, $type->namespace, null); } } return array(null, null, null, null); @@ -872,45 +963,60 @@ class SOAP_WSDL extends SOAP_Base class SOAP_WSDL_Cache extends SOAP_Base { - // Cache settings - /** - * Use WSDL cache + * Use WSDL cache? * * @var boolean */ - var $_cacheUse = null; + var $_cacheUse; /** - * Cache max lifetime (in seconds) + * WSDL cache directory. * - * @var int + * @var string */ - var $_cacheMaxAge = null; + var $_cacheDir; /** - * SOAP_WSDL_Cache constructor + * Cache maximum lifetime (in seconds) * - * @param boolean use caching - * @param int cache max lifetime (in seconds) - * @access public + * @var integer */ - function SOAP_WSDL_Cache($cacheUse = WSDL_CACHE_USE, - $cacheMaxAge = WSDL_CACHE_MAX_AGE) + var $_cacheMaxAge; + + /** + * Constructor. + * + * @param boolean $cashUse Use caching? + * @param integer $cacheMaxAge Cache maximum lifetime (in seconds) + */ + function SOAP_WSDL_Cache($cacheUse = false, + $cacheMaxAge = WSDL_CACHE_MAX_AGE, + $cacheDir = null) { parent::SOAP_Base('WSDLCACHE'); $this->_cacheUse = $cacheUse; + $this->_cacheDir = $cacheDir; $this->_cacheMaxAge = $cacheMaxAge; } /** - * _cacheDir - * return the path to the cache, if it doesn't exist, make it + * Returns the path to the cache and creates it, if it doesn't exist. + * + * @private + * + * @return string The directory to use for the cache. */ function _cacheDir() { - $dir = getenv("WSDLCACHE"); - if (!$dir) $dir = " ./wsdlcache"; + if (!empty($this->_cacheDir)) { + $dir = $this->_cacheDir; + } else { + $dir = getenv('WSDLCACHE'); + if (empty($dir)) { + $dir = './wsdlcache'; + } + } @mkdir($dir, 0700); return $dir; } @@ -930,15 +1036,10 @@ class SOAP_WSDL_Cache extends SOAP_Base $cachename = $md5_wsdl = $file_data = ''; if ($this->_cacheUse) { // Try to retrieve WSDL from cache - $cachename = SOAP_WSDL_Cache::_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl'; - if (file_exists($cachename)) { - $wf = fopen($cachename, 'rb'); - if ($wf) { - // Reading cached file - $file_data = fread($wf, filesize($cachename)); - $md5_wsdl = md5($file_data); - fclose($wf); - } + $cachename = $this->_cacheDir() . '/' . md5($wsdl_fname). ' .wsdl'; + if (file_exists($cachename) && + $file_data = file_get_contents($cachename)) { + $md5_wsdl = md5($file_data); if ($cache) { if ($cache != $md5_wsdl) { return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname); @@ -946,32 +1047,28 @@ class SOAP_WSDL_Cache extends SOAP_Base } else { $fi = stat($cachename); $cache_mtime = $fi[8]; - //print cache_mtime, time() if ($cache_mtime + $this->_cacheMaxAge < time()) { - // expired - $md5_wsdl = ''; // refetch + // Expired, refetch. + $md5_wsdl = ''; } } } } + // Not cached or not using cache. Retrieve WSDL from URL if (!$md5_wsdl) { - // Not cached or not using cache. Retrieve WSDL from URL - - // is it a local file? - // this section should be replace by curl at some point - if (!preg_match('/^(https?|file):\/\//', $wsdl_fname)) { + // Is it a local file? + if (strpos($wsdl_fname, 'file://') === 0) { + $wsdl_fname = substr($wsdl_fname, 7); if (!file_exists($wsdl_fname)) { - return $this->_raiseSoapFault("Unable to read local WSDL $wsdl_fname", $wsdl_fname); - } - if (function_exists('file_get_contents')) { - $file_data = file_get_contents($wsdl_fname); - } else { - $file_data = implode('',file($wsdl_fname)); + return $this->_raiseSoapFault('Unable to read local WSDL file', $wsdl_fname); } + $file_data = file_get_contents($wsdl_fname); + } elseif (!preg_match('|^https?://|', $wsdl_fname)) { + return $this->_raiseSoapFault('Unknown schema of WSDL URL', $wsdl_fname); } else { $uri = explode('?', $wsdl_fname); - $rq =& new HTTP_Request($uri[0], $proxy_params); + $rq = new HTTP_Request($uri[0], $proxy_params); // the user agent HTTP_Request uses fouls things up if (isset($uri[1])) { $rq->addRawQueryString($uri[1]); @@ -981,11 +1078,14 @@ class SOAP_WSDL_Cache extends SOAP_Base isset($proxy_params['proxy_port']) && isset($proxy_params['proxy_user']) && isset($proxy_params['proxy_pass'])) { - $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port'], - $proxy_params['proxy_user'], $proxy_params['proxy_pass']); + $rq->setProxy($proxy_params['proxy_host'], + $proxy_params['proxy_port'], + $proxy_params['proxy_user'], + $proxy_params['proxy_pass']); } elseif (isset($proxy_params['proxy_host']) && isset($proxy_params['proxy_port'])) { - $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port']); + $rq->setProxy($proxy_params['proxy_host'], + $proxy_params['proxy_port']); } $result = $rq->sendRequest(); @@ -1006,9 +1106,11 @@ class SOAP_WSDL_Cache extends SOAP_Base fclose($fp); } } + if ($this->_cacheUse && $cache && $cache != $md5_wsdl) { - return $this->_raiseSoapFault("WSDL Checksum error!", $wsdl_fname); + return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname); } + return $file_data; } @@ -1049,12 +1151,14 @@ class SOAP_WSDL_Parser extends SOAP_Base var $currentElement; /** - * constructor + * Constructor. */ function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false) { parent::SOAP_Base('WSDLPARSER'); - $this->cache =& new SOAP_WSDL_Cache($wsdl->cacheUse, $wsdl->cacheMaxAge); + $this->cache =& new SOAP_WSDL_Cache($wsdl->cacheUse, + $wsdl->cacheMaxAge, + $wsdl->cacheDir); $this->uri = $uri; $this->wsdl = &$wsdl; $this->docs = $docs; @@ -1094,7 +1198,7 @@ class SOAP_WSDL_Parser extends SOAP_Base function startElement($parser, $name, $attrs) { // Get element prefix. - $qname =& new QName($name); + $qname = new QName($name); if ($qname->ns) { $ns = $qname->ns; if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) { @@ -1142,7 +1246,7 @@ class SOAP_WSDL_Parser extends SOAP_Base } $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs; if (array_key_exists('base', $attrs)) { - $qn =& new QName($attrs['base']); + $qn = new QName($attrs['base']); $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name; $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns; } else { @@ -1156,7 +1260,7 @@ class SOAP_WSDL_Parser extends SOAP_Base case 'element': if (isset($attrs['type'])) { - $qn =& new QName($attrs['type']); + $qn = new QName($attrs['type']); $attrs['type'] = $qn->name; if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) { $attrs['namespace'] = $qn->ns; @@ -1170,7 +1274,7 @@ class SOAP_WSDL_Parser extends SOAP_Base } if (isset($attrs['ref'])) { - $qn =& new QName($attrs['ref']); + $qn = new QName($attrs['ref']); $this->currentElement = $qn->name; } else { $this->currentElement = $attrs['name']; @@ -1205,7 +1309,7 @@ class SOAP_WSDL_Parser extends SOAP_Base case 'restriction': if ($this->schemaStatus == 'complexType') { if (!empty($attrs['base'])) { - $qn =& new QName($attrs['base']); + $qn = new QName($attrs['base']); $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name; // Types that extend from other types aren't @@ -1250,10 +1354,10 @@ class SOAP_WSDL_Parser extends SOAP_Base $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs; } else { if (isset($attrs['ref'])) { - $q =& new QName($attrs['ref']); + $q = new QName($attrs['ref']); foreach ($attrs as $k => $v) { if ($k != 'ref' && strstr($k, $q->name)) { - $vq =& new QName($v); + $vq = new QName($v); if ($q->name == 'arrayType') { $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo; $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; @@ -1278,9 +1382,9 @@ class SOAP_WSDL_Parser extends SOAP_Base case 'part': $qn = null; if (isset($attrs['type'])) { - $qn =& new QName($attrs['type']); + $qn = new QName($attrs['type']); } elseif (isset($attrs['element'])) { - $qn =& new QName($attrs['element']); + $qn = new QName($attrs['element']); } if ($qn) { $attrs['type'] = $qn->name; @@ -1319,7 +1423,7 @@ class SOAP_WSDL_Parser extends SOAP_Base $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs; } if (array_key_exists('message', $attrs)) { - $qn =& new QName($attrs['message']); + $qn = new QName($attrs['message']); $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name; $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns; } @@ -1338,6 +1442,7 @@ class SOAP_WSDL_Parser extends SOAP_Base $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; switch ($ns) { case SCHEMA_SOAP: + case SCHEMA_SOAP12: // this deals with wsdl section 3 soap binding switch ($qname->name) { case 'binding': @@ -1529,7 +1634,7 @@ class SOAP_WSDL_Parser extends SOAP_Base $this->currentPort = $attrs['name']; $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs; // XXX hack to deal with binding namespaces - $qn =& new QName($attrs['binding']); + $qn = new QName($attrs['binding']); $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name; $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns; break; @@ -1632,7 +1737,7 @@ class SOAP_WSDL_Parser extends SOAP_Base } $this->status = 'binding'; $this->currentBinding = $attrs['name']; - $qn =& new QName($attrs['type']); + $qn = new QName($attrs['type']); $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name; $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns; break; @@ -1651,7 +1756,7 @@ class SOAP_WSDL_Parser extends SOAP_Base $this->wsdl->definition = $attrs; foreach ($attrs as $key => $value) { if (strstr($key, 'xmlns:') !== false) { - $qn =& new QName($key); + $qn = new QName($key); // XXX need to refactor ns handling. $this->wsdl->namespaces[$qn->name] = $value; $this->wsdl->ns[$value] = $qn->name; @@ -1866,15 +1971,21 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base */ var $wsdl = null; - /** Constructor + /** + * Constructor. * - * @param $objects Reference to the object or array of objects to parse - * @param $wsdl Reference to the SOAP_WSDL object to populate - * @param $targetNamespace The target namespace of schema types etc. - * @param $service_name Name of the WSDL - * @param $service_desc Optional description of the WSDL + * @param object|array $objects Reference to the object or array of + * objects to parse. + * @param SOAP_WSDL $wsdl Reference to the SOAP_WSDL object to + * populate. + * @param string $targetNamespace The target namespace of schema types + * etc. + * @param string $service_name Name of the WSDL . + * @param string $service_desc Optional description of the WSDL + * . */ - function SOAP_WSDL_ObjectParser(&$objects, &$wsdl, $targetNamespace, $service_name, $service_desc = '') + function SOAP_WSDL_ObjectParser($objects, &$wsdl, $targetNamespace, + $service_name, $service_desc = '') { parent::SOAP_Base('WSDLOBJECTPARSER'); @@ -1884,14 +1995,17 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base $this->_initialise($service_name); // Parse each web service object - $wsdl_ref = (is_array($objects)? $objects : array(&$objects)); + $wsdl_ref = is_array($objects) ? $objects : array($objects); foreach ($wsdl_ref as $ref_item) { - if (!is_object($ref_item)) - return $this->_raiseSoapFault('Invalid web service object passed to object parser', 'urn:' . get_class($object)); + if (!is_object($ref_item)) { + $this->_raiseSoapFault('Invalid web service object passed to object parser'); + continue; + } - if ($this->_parse($ref_item, $targetNamespace, $service_name) != true) + if (!$this->_parse($ref_item, $targetNamespace, $service_name)) { break; + } } // Build bindings from abstract data. @@ -1916,7 +2030,7 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base $this->wsdl->namespaces['soap'] = SCHEMA_SOAP; // WSDL SOAP bindings $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema - $this->wsdl->namespaces['SOAP-ENC'] = array_search('SOAP-ENC', $this->_namespaces); // SOAP types + $this->wsdl->namespaces[SOAP_BASE::SOAPENCPrefix()] = array_search(SOAP_BASE::SOAPENCPrefix(), $this->_namespaces); // SOAP types // XXX Refactor $namespace/$ns for Shane :-) unset($this->wsdl->ns['urn:' . $service_name]); @@ -1927,32 +2041,27 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base } /** - * Parser - takes a single object to add to tree - * (non-destructive). + * Parser - takes a single object to add to tree (non-destructive). * - * @param $object Reference to the object to parse - * @param $service_name Name of the WSDL - * @access private + * @access private + * + * @param object $object Reference to the object to parse. + * @param string $schemaNamespace + * @param string $service_name Name of the WSDL . */ - function _parse(&$object, $schemaNamespace, $service_name) + function _parse($object, $schemaNamespace, $service_name) { // Create namespace prefix for the schema - // XXX not very elegant :-( - - list($schPrefix, $foo) = $this->_getTypeNs('{' . $schemaNamespace.'}'); - unset($foo); + list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}'); // Parse all the types defined by the object in whatever // schema language we are using (currently __typedef arrays) // *** *** - foreach ($object->__typedef as $typeName => $typeValue) { // Get/create namespace definition - list($nsPrefix, $typeName) = $this->_getTypeNs($typeName); // Create type definition - $this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName); $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName]; @@ -1960,19 +2069,17 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base // flavors: // Array = array(array("item" => "value")) // Struct = array("item1" => "value1", "item2" => "value2", ...) - if (is_array($typeValue)) { if (is_array(current($typeValue)) && count($typeValue) == 1 && count(current($typeValue)) == 1) { // It's an array - $thisType['type'] = 'Array'; - list($nsPrefix, $typeName) = $this->_getTypeNs(current(current($typeValue))); + $nsType = current(current($typeValue)); + list($nsPrefix, $typeName) = $this->_getTypeNs($nsType); $thisType['namespace'] = $nsPrefix; $thisType['arrayType'] = $typeName . '[]'; } elseif (!is_array(current($typeValue))) { // It's a struct - $thisType['type'] = 'Struct'; $thisType['order'] = 'all'; $thisType['namespace'] = $nsPrefix; @@ -1996,13 +2103,15 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base // Create an empty element array with the target namespace // prefix, to match the results of WSDL parsing. - $this->wsdl->elements[$schPrefix] = array(); // Populate tree with message information // *** *** - foreach ($object->__dispatch_map as $operationName => $messages) { + // We need at least 'in' and 'out' parameters. + if (!isset($messages['in']) || !isset($messages['out'])) { + return $this->_raiseSoapFault('The dispatch map for the method "' . $operationName . '" is missing an "in" or "out" definition.', 'urn:' . get_class($object)); + } foreach ($messages as $messageType => $messageParts) { unset($thisMessage); @@ -2044,7 +2153,6 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base // XXX Current implementation only supports one portType that // encompasses all of the operations available. // *** *** - if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) { $this->wsdl->portTypes[$service_name . 'Port'] = array(); } @@ -2074,24 +2182,25 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base } /** - * Take all the abstract WSDL data and build concrete bindings and + * Takes all the abstract WSDL data and builds concrete bindings and * services (destructive). * - * XXX Current implementation discards $service_desc. - * - * @param $schemaNamespace Namespace for types etc. - * @param $service_name Name of the WSDL - * @param $service_desc Optional description of the WSDL * @access private + * @todo Current implementation discards $service_desc. + * + * @param string $schemaNamespace Namespace for types etc. + * @param string $service_name Name of the WSDL . + * @param string $service_desc Optional description of the WSDL + * . */ - function _generateBindingsAndServices($schemaNamespace, $service_name, $service_desc = '') + function _generateBindingsAndServices($schemaNamespace, $service_name, + $service_desc = '') { // Populate tree with bindings information // XXX Current implementation only supports one binding that // matches the single portType and all of its operations. // XXX Is this the correct use of $schemaNamespace here? // *** *** - $this->wsdl->bindings[$service_name . 'Binding'] = array( 'type' => $service_name . 'Port', 'namespace' => $this->tnsPrefix, @@ -2117,11 +2226,12 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base // Populate tree with service information // XXX Current implementation supports one service which groups // all of the ports together, one port per binding - // XXX What about https? // *** *** $this->wsdl->services[$service_name . 'Service'] = array('ports' => array()); $thisService =& $this->wsdl->services[$service_name . 'Service']['ports']; + $https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) || + getenv('SSL_PROTOCOL_VERSION'); foreach ($this->wsdl->bindings as $bindingName => $bindingData) { $thisService[$bindingData['type']] = array( @@ -2129,7 +2239,8 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base 'binding' => $bindingName, 'namespace' => $this->tnsPrefix, 'address' => array('location' => - 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . + ($https ? 'https://' : 'http://') . + $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')), 'type' => 'soap'); } @@ -2164,15 +2275,15 @@ class SOAP_WSDL_ObjectParser extends SOAP_Base */ function _getTypeNs($type) { - preg_match_all("'\{(.*)\}'sm", $type, $m); - if (isset($m[1][0]) && $m[1][0] != '') { - if (!array_key_exists($m[1][0], $this->wsdl->ns)) { + preg_match_all('/\{(.*)\}/sm', $type, $m); + if (!empty($m[1][0])) { + if (!isset($this->wsdl->ns[$m[1][0]])) { $ns_pref = 'ns' . count($this->wsdl->namespaces); $this->wsdl->ns[$m[1][0]] = $ns_pref; $this->wsdl->namespaces[$ns_pref] = $m[1][0]; } $typens = $this->wsdl->ns[$m[1][0]]; - $type = ereg_replace($m[0][0], '', $type); + $type = str_replace($m[0][0], '', $type); } else { $typens = 'xsd'; } diff --git a/thirdparty/pear/SOAP/example/attachment.php b/thirdparty/pear/SOAP/example/attachment.php old mode 100644 new mode 100755 index 16533b0..16533b0 --- a/thirdparty/pear/SOAP/example/attachment.php +++ b/thirdparty/pear/SOAP/example/attachment.php diff --git a/thirdparty/pear/SOAP/example/client.php b/thirdparty/pear/SOAP/example/client.php old mode 100644 new mode 100755 index 06965ec..06965ec --- a/thirdparty/pear/SOAP/example/client.php +++ b/thirdparty/pear/SOAP/example/client.php diff --git a/thirdparty/pear/SOAP/example/com_client.php b/thirdparty/pear/SOAP/example/com_client.php old mode 100644 new mode 100755 index 3f33daf..3f33daf --- a/thirdparty/pear/SOAP/example/com_client.php +++ b/thirdparty/pear/SOAP/example/com_client.php diff --git a/thirdparty/pear/SOAP/example/disco_server.php b/thirdparty/pear/SOAP/example/disco_server.php old mode 100644 new mode 100755 index 21a9018..21a9018 --- a/thirdparty/pear/SOAP/example/disco_server.php +++ b/thirdparty/pear/SOAP/example/disco_server.php diff --git a/thirdparty/pear/SOAP/example/email_client.php b/thirdparty/pear/SOAP/example/email_client.php old mode 100644 new mode 100755 index 797906f..797906f --- a/thirdparty/pear/SOAP/example/email_client.php +++ b/thirdparty/pear/SOAP/example/email_client.php diff --git a/thirdparty/pear/SOAP/example/email_gateway.php b/thirdparty/pear/SOAP/example/email_gateway.php old mode 100644 new mode 100755 index 42701ed..42701ed --- a/thirdparty/pear/SOAP/example/email_gateway.php +++ b/thirdparty/pear/SOAP/example/email_gateway.php diff --git a/thirdparty/pear/SOAP/example/email_pop_gateway.php b/thirdparty/pear/SOAP/example/email_pop_gateway.php old mode 100644 new mode 100755 index bd9ff38..bd9ff38 --- a/thirdparty/pear/SOAP/example/email_pop_gateway.php +++ b/thirdparty/pear/SOAP/example/email_pop_gateway.php diff --git a/thirdparty/pear/SOAP/example/email_pop_server.php b/thirdparty/pear/SOAP/example/email_pop_server.php old mode 100644 new mode 100755 index be47873..be47873 --- a/thirdparty/pear/SOAP/example/email_pop_server.php +++ b/thirdparty/pear/SOAP/example/email_pop_server.php diff --git a/thirdparty/pear/SOAP/example/email_server.php b/thirdparty/pear/SOAP/example/email_server.php old mode 100644 new mode 100755 index cad3dea..cad3dea --- a/thirdparty/pear/SOAP/example/email_server.php +++ b/thirdparty/pear/SOAP/example/email_server.php diff --git a/thirdparty/pear/SOAP/example/example_server.php b/thirdparty/pear/SOAP/example/example_server.php old mode 100644 new mode 100755 index 376b324..376b324 --- a/thirdparty/pear/SOAP/example/example_server.php +++ b/thirdparty/pear/SOAP/example/example_server.php diff --git a/thirdparty/pear/SOAP/example/example_types.php b/thirdparty/pear/SOAP/example/example_types.php old mode 100644 new mode 100755 index fee55ac..fee55ac --- a/thirdparty/pear/SOAP/example/example_types.php +++ b/thirdparty/pear/SOAP/example/example_types.php diff --git a/thirdparty/pear/SOAP/example/server.php b/thirdparty/pear/SOAP/example/server.php old mode 100644 new mode 100755 index 543bff3..543bff3 --- a/thirdparty/pear/SOAP/example/server.php +++ b/thirdparty/pear/SOAP/example/server.php diff --git a/thirdparty/pear/SOAP/example/server2.php b/thirdparty/pear/SOAP/example/server2.php old mode 100644 new mode 100755 index 3d945b5..3d945b5 --- a/thirdparty/pear/SOAP/example/server2.php +++ b/thirdparty/pear/SOAP/example/server2.php diff --git a/thirdparty/pear/SOAP/example/smtp.php b/thirdparty/pear/SOAP/example/smtp.php old mode 100644 new mode 100755 index 7a2695e..7a2695e --- a/thirdparty/pear/SOAP/example/smtp.php +++ b/thirdparty/pear/SOAP/example/smtp.php diff --git a/thirdparty/pear/SOAP/example/stockquote.php b/thirdparty/pear/SOAP/example/stockquote.php old mode 100644 new mode 100755 index 04749bc..04749bc --- a/thirdparty/pear/SOAP/example/stockquote.php +++ b/thirdparty/pear/SOAP/example/stockquote.php diff --git a/thirdparty/pear/SOAP/example/tcp_client.php b/thirdparty/pear/SOAP/example/tcp_client.php old mode 100644 new mode 100755 index f583f7f..f583f7f --- a/thirdparty/pear/SOAP/example/tcp_client.php +++ b/thirdparty/pear/SOAP/example/tcp_client.php diff --git a/thirdparty/pear/SOAP/example/tcp_daemon.pl b/thirdparty/pear/SOAP/example/tcp_daemon.pl old mode 100644 new mode 100755 index 3633f4c..3633f4c --- a/thirdparty/pear/SOAP/example/tcp_daemon.pl +++ b/thirdparty/pear/SOAP/example/tcp_daemon.pl diff --git a/thirdparty/pear/SOAP/example/tcp_server.php b/thirdparty/pear/SOAP/example/tcp_server.php old mode 100644 new mode 100755 index e1ceda9..e1ceda9 --- a/thirdparty/pear/SOAP/example/tcp_server.php +++ b/thirdparty/pear/SOAP/example/tcp_server.php diff --git a/thirdparty/pear/SOAP/example/wsdl_client.php b/thirdparty/pear/SOAP/example/wsdl_client.php old mode 100644 new mode 100755 index 4af4bf4..4af4bf4 --- a/thirdparty/pear/SOAP/example/wsdl_client.php +++ b/thirdparty/pear/SOAP/example/wsdl_client.php diff --git a/thirdparty/pear/SOAP/tools/genproxy.php b/thirdparty/pear/SOAP/tools/genproxy.php old mode 100644 new mode 100755 index 8a334e1..8a334e1 --- a/thirdparty/pear/SOAP/tools/genproxy.php +++ b/thirdparty/pear/SOAP/tools/genproxy.php diff --git a/thirdparty/pear/System.php b/thirdparty/pear/System.php old mode 100644 new mode 100755 index 7289016..69cc7ac --- a/thirdparty/pear/System.php +++ b/thirdparty/pear/System.php @@ -1,24 +1,22 @@ | -// +----------------------------------------------------------------------+ -// -// $Id$ -// +/** + * File/Directory manipulation + * + * PHP versions 4 and 5 + * + * @category pear + * @package System + * @author Tomas V.V.Cox + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version CVS: $Id: System.php 276386 2009-02-24 23:52:56Z dufuz $ + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ +/** + * base class + */ require_once 'PEAR.php'; require_once 'Console/Getopt.php'; @@ -31,7 +29,9 @@ $GLOBALS['_System_temp_files'] = array(); * Unix and Windows. The names and usage has been taken from its respectively * GNU commands. The functions will return (bool) false on error and will * trigger the error with the PHP trigger_error() function (you can silence -* the error by prefixing a '@' sign after the function call). +* the error by prefixing a '@' sign after the function call, but this +* is not recommended practice. Instead use an error handler with +* {@link set_error_handler()}). * * Documentation on this class you can find in: * http://pear.php.net/manual/ @@ -46,39 +46,45 @@ $GLOBALS['_System_temp_files'] = array(); * * System::rm(array('-r', $file1, $dir1)); * -* @package System -* @author Tomas V.V.Cox -* @version $Revision$ -* @access public -* @see http://pear.php.net/manual/ +* @category pear +* @package System +* @author Tomas V.V. Cox +* @copyright 1997-2006 The PHP Group +* @license http://opensource.org/licenses/bsd-license.php New BSD License +* @version Release: 1.9.0 +* @link http://pear.php.net/package/PEAR +* @since Class available since Release 0.1 +* @static */ class System { /** - * returns the commandline arguments of a function - * - * @param string $argv the commandline - * @param string $short_options the allowed option short-tags - * @param string $long_options the allowed option long-tags - * @return array the given options and there values - * @access private - */ + * returns the commandline arguments of a function + * + * @param string $argv the commandline + * @param string $short_options the allowed option short-tags + * @param string $long_options the allowed option long-tags + * @return array the given options and there values + * @static + * @access private + */ function _parseArgs($argv, $short_options, $long_options = null) { if (!is_array($argv) && $argv !== null) { - $argv = preg_split('/\s+/', $argv); + $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY); } return Console_Getopt::getopt2($argv, $short_options); } /** - * Output errors with PHP trigger_error(). You can silence the errors - * with prefixing a "@" sign to the function call: @System::mkdir(..); - * - * @param mixed $error a PEAR error or a string with the error message - * @return bool false - * @access private - */ + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @System::mkdir(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + * @static + * @access private + */ function raiseError($error) { if (PEAR::isError($error)) { @@ -89,96 +95,106 @@ class System } /** - * Creates a nested array representing the structure of a directory - * - * System::_dirToStruct('dir1', 0) => - * Array - * ( - * [dirs] => Array - * ( - * [0] => dir1 - * ) - * - * [files] => Array - * ( - * [0] => dir1/file2 - * [1] => dir1/file3 - * ) - * ) - * @param string $sPath Name of the directory - * @param integer $maxinst max. deep of the lookup - * @param integer $aktinst starting deep of the lookup - * @return array the structure of the dir - * @access private - */ - - function _dirToStruct($sPath, $maxinst, $aktinst = 0) + * Creates a nested array representing the structure of a directory + * + * System::_dirToStruct('dir1', 0) => + * Array + * ( + * [dirs] => Array + * ( + * [0] => dir1 + * ) + * + * [files] => Array + * ( + * [0] => dir1/file2 + * [1] => dir1/file3 + * ) + * ) + * @param string $sPath Name of the directory + * @param integer $maxinst max. deep of the lookup + * @param integer $aktinst starting deep of the lookup + * @param bool $silent if true, do not emit errors. + * @return array the structure of the dir + * @static + * @access private + */ + function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false) { $struct = array('dirs' => array(), 'files' => array()); if (($dir = @opendir($sPath)) === false) { - System::raiseError("Could not open dir $sPath"); + if (!$silent) { + System::raiseError("Could not open dir $sPath"); + } return $struct; // XXX could not open error } - $struct['dirs'][] = $sPath; // XXX don't add if '.' or '..' ? + + $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? $list = array(); - while ($file = readdir($dir)) { + while (false !== ($file = readdir($dir))) { if ($file != '.' && $file != '..') { $list[] = $file; } } + closedir($dir); - sort($list); + natsort($list); if ($aktinst < $maxinst || $maxinst == 0) { - foreach($list as $val) { + foreach ($list as $val) { $path = $sPath . DIRECTORY_SEPARATOR . $val; - if (is_dir($path)) { - $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1); - $struct = array_merge_recursive($tmp, $struct); + if (is_dir($path) && !is_link($path)) { + $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent); + $struct = array_merge_recursive($struct, $tmp); } else { $struct['files'][] = $path; } } } + return $struct; } /** - * Creates a nested array representing the structure of a directory and files - * - * @param array $files Array listing files and dirs - * @return array - * @see System::_dirToStruct() - */ + * Creates a nested array representing the structure of a directory and files + * + * @param array $files Array listing files and dirs + * @return array + * @static + * @see System::_dirToStruct() + */ function _multipleToStruct($files) { $struct = array('dirs' => array(), 'files' => array()); settype($files, 'array'); foreach ($files as $file) { - if (is_dir($file)) { - $tmp = System::_dirToStruct($file, 0); + if (is_dir($file) && !is_link($file)) { + $tmp = System::_dirToStruct($file, 0); $struct = array_merge_recursive($tmp, $struct); } else { - $struct['files'][] = $file; + if (!in_array($file, $struct['files'])) { + $struct['files'][] = $file; + } } } return $struct; } /** - * The rm command for removing files. - * Supports multiple files and dirs and also recursive deletes - * - * @param string $args the arguments for rm - * @return mixed PEAR_Error or true for success - * @access public - */ + * The rm command for removing files. + * Supports multiple files and dirs and also recursive deletes + * + * @param string $args the arguments for rm + * @return mixed PEAR_Error or true for success + * @static + * @access public + */ function rm($args) { - $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-) + $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-) if (PEAR::isError($opts)) { return System::raiseError($opts); } - foreach($opts[0] as $opt) { + foreach ($opts[0] as $opt) { if ($opt[0] == 'r') { $do_recursive = true; } @@ -186,12 +202,14 @@ class System $ret = true; if (isset($do_recursive)) { $struct = System::_multipleToStruct($opts[1]); - foreach($struct['files'] as $file) { + foreach ($struct['files'] as $file) { if (!@unlink($file)) { $ret = false; } } - foreach($struct['dirs'] as $dir) { + + rsort($struct['dirs']); + foreach ($struct['dirs'] as $dir) { if (!@rmdir($dir)) { $ret = false; } @@ -208,24 +226,26 @@ class System } /** - * Make directories. Note that we use call_user_func('mkdir') to avoid - * a problem with ZE2 calling System::mkDir instead of the native PHP func. - * - * @param string $args the name of the director(y|ies) to create - * @return bool True for success - * @access public - */ + * Make directories. + * + * The -p option will create parent directories + * @param string $args the name of the director(y|ies) to create + * @return bool True for success + * @static + * @access public + */ function mkDir($args) { $opts = System::_parseArgs($args, 'pm:'); if (PEAR::isError($opts)) { return System::raiseError($opts); } + $mode = 0777; // default mode - foreach($opts[0] as $opt) { + foreach ($opts[0] as $opt) { if ($opt[0] == 'p') { $create_parents = true; - } elseif($opt[0] == 'm') { + } elseif ($opt[0] == 'm') { // if the mode is clearly an octal number (starts with 0) // convert it to decimal if (strlen($opt[1]) && $opt[1]{0} == '0') { @@ -237,52 +257,64 @@ class System $mode = $opt[1]; } } + $ret = true; if (isset($create_parents)) { - foreach($opts[1] as $dir) { + foreach ($opts[1] as $dir) { $dirstack = array(); - while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) { + while ((!file_exists($dir) || !is_dir($dir)) && + $dir != DIRECTORY_SEPARATOR) { array_unshift($dirstack, $dir); $dir = dirname($dir); } + while ($newdir = array_shift($dirstack)) { - if (!call_user_func('mkdir', $newdir, $mode)) { + if (!is_writeable(dirname($newdir))) { + $ret = false; + break; + } + + if (!mkdir($newdir, $mode)) { $ret = false; } } } } else { foreach($opts[1] as $dir) { - if (!@is_dir($dir) && !call_user_func('mkdir', $dir, $mode)) { + if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) { $ret = false; } } } + return $ret; } /** - * Concatenate files - * - * Usage: - * 1) $var = System::cat('sample.txt test.txt'); - * 2) System::cat('sample.txt test.txt > final.txt'); - * 3) System::cat('sample.txt test.txt >> final.txt'); - * - * Note: as the class use fopen, urls should work also (test that) - * - * @param string $args the arguments - * @return boolean true on success - * @access public - */ + * Concatenate files + * + * Usage: + * 1) $var = System::cat('sample.txt test.txt'); + * 2) System::cat('sample.txt test.txt > final.txt'); + * 3) System::cat('sample.txt test.txt >> final.txt'); + * + * Note: as the class use fopen, urls should work also (test that) + * + * @param string $args the arguments + * @return boolean true on success + * @static + * @access public + */ function &cat($args) { $ret = null; $files = array(); if (!is_array($args)) { - $args = preg_split('/\s+/', $args); + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); } - for($i=0; $i < count($args); $i++) { + + $count_args = count($args); + for ($i = 0; $i < $count_args; $i++) { if ($args[$i] == '>') { $mode = 'wb'; $outputfile = $args[$i+1]; @@ -295,6 +327,7 @@ class System $files[] = $args[$i]; } } + $outputfd = false; if (isset($mode)) { if (!$outputfd = fopen($outputfile, $mode)) { $err = System::raiseError("Could not open $outputfile"); @@ -308,7 +341,7 @@ class System continue; } while ($cont = fread($fd, 2048)) { - if (isset($outputfd)) { + if (is_resource($outputfd)) { fwrite($outputfd, $cont); } else { $ret .= $cont; @@ -316,35 +349,36 @@ class System } fclose($fd); } - if (@is_resource($outputfd)) { + if (is_resource($outputfd)) { fclose($outputfd); } return $ret; } /** - * Creates temporary files or directories. This function will remove - * the created files when the scripts finish its execution. - * - * Usage: - * 1) $tempfile = System::mktemp("prefix"); - * 2) $tempdir = System::mktemp("-d prefix"); - * 3) $tempfile = System::mktemp(); - * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); - * - * prefix -> The string that will be prepended to the temp name - * (defaults to "tmp"). - * -d -> A temporary dir will be created instead of a file. - * -t -> The target dir where the temporary (file|dir) will be created. If - * this param is missing by default the env vars TMP on Windows or - * TMPDIR in Unix will be used. If these vars are also missing - * c:\windows\temp or /tmp will be used. - * - * @param string $args The arguments - * @return mixed the full path of the created (file|dir) or false - * @see System::tmpdir() - * @access public - */ + * Creates temporary files or directories. This function will remove + * the created files when the scripts finish its execution. + * + * Usage: + * 1) $tempfile = System::mktemp("prefix"); + * 2) $tempdir = System::mktemp("-d prefix"); + * 3) $tempfile = System::mktemp(); + * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); + * + * prefix -> The string that will be prepended to the temp name + * (defaults to "tmp"). + * -d -> A temporary dir will be created instead of a file. + * -t -> The target dir where the temporary (file|dir) will be created. If + * this param is missing by default the env vars TMP on Windows or + * TMPDIR in Unix will be used. If these vars are also missing + * c:\windows\temp or /tmp will be used. + * + * @param string $args The arguments + * @return mixed the full path of the created (file|dir) or false + * @see System::tmpdir() + * @static + * @access public + */ function mktemp($args = null) { static $first_time = true; @@ -352,65 +386,81 @@ class System if (PEAR::isError($opts)) { return System::raiseError($opts); } - foreach($opts[0] as $opt) { - if($opt[0] == 'd') { + + foreach ($opts[0] as $opt) { + if ($opt[0] == 'd') { $tmp_is_dir = true; - } elseif($opt[0] == 't') { + } elseif ($opt[0] == 't') { $tmpdir = $opt[1]; } } + $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; if (!isset($tmpdir)) { $tmpdir = System::tmpdir(); } - if (!System::mkDir("-p $tmpdir")) { + + if (!System::mkDir(array('-p', $tmpdir))) { return false; } + $tmp = tempnam($tmpdir, $prefix); if (isset($tmp_is_dir)) { unlink($tmp); // be careful possible race condition here - if (!call_user_func('mkdir', $tmp, 0700)) { + if (!mkdir($tmp, 0700)) { return System::raiseError("Unable to create temporary directory $tmpdir"); } } + $GLOBALS['_System_temp_files'][] = $tmp; + if (isset($tmp_is_dir)) { + //$GLOBALS['_System_temp_files'][] = dirname($tmp); + } + if ($first_time) { PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); $first_time = false; } + return $tmp; } /** - * Remove temporary files created my mkTemp. This function is executed - * at script shutdown time - * - * @access private - */ + * Remove temporary files created my mkTemp. This function is executed + * at script shutdown time + * + * @static + * @access private + */ function _removeTmpFiles() { if (count($GLOBALS['_System_temp_files'])) { $delete = $GLOBALS['_System_temp_files']; array_unshift($delete, '-r'); System::rm($delete); + $GLOBALS['_System_temp_files'] = array(); } } /** - * Get the path of the temporal directory set in the system - * by looking in its environments variables. - * Note: php.ini-recommended removes the "E" from the variables_order setting, - * making unavaible the $_ENV array, that s why we do tests with _ENV - * - * @return string The temporal directory on the system - */ + * Get the path of the temporal directory set in the system + * by looking in its environments variables. + * Note: php.ini-recommended removes the "E" from the variables_order setting, + * making unavaible the $_ENV array, that s why we do tests with _ENV + * + * @static + * @return string The temporary directory on the system + */ function tmpdir() { if (OS_WINDOWS) { + if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { + return $var; + } if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { return $var; } - if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { + if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) { return $var; } if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { @@ -421,38 +471,60 @@ class System if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { return $var; } - return '/tmp'; + return realpath('/tmp'); } /** - * The "which" command (show the full path of a command) - * - * @param string $program The command to search for - * @return mixed A string with the full path or false if not found - * @author Stig Bakken - */ + * The "which" command (show the full path of a command) + * + * @param string $program The command to search for + * @param mixed $fallback Value to return if $program is not found + * + * @return mixed A string with the full path or false if not found + * @static + * @author Stig Bakken + */ function which($program, $fallback = false) { - // is_executable() is not available on windows - if (OS_WINDOWS) { - $pear_is_executable = 'is_file'; - } else { - $pear_is_executable = 'is_executable'; + // enforce API + if (!is_string($program) || '' == $program) { + return $fallback; } // full path given if (basename($program) != $program) { - return (@$pear_is_executable($program)) ? $program : $fallback; + $path_elements[] = dirname($program); + $program = basename($program); + } else { + // Honor safe mode + if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) { + $path = getenv('PATH'); + if (!$path) { + $path = getenv('Path'); // some OSes are just stupid enough to do this + } + } + $path_elements = explode(PATH_SEPARATOR, $path); + } + + if (OS_WINDOWS) { + $exe_suffixes = getenv('PATHEXT') + ? explode(PATH_SEPARATOR, getenv('PATHEXT')) + : array('.exe','.bat','.cmd','.com'); + // allow passing a command.exe param + if (strpos($program, '.') !== false) { + array_unshift($exe_suffixes, ''); + } + // is_executable() is not available on windows for PHP4 + $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file'; + } else { + $exe_suffixes = array(''); + $pear_is_executable = 'is_executable'; } - // XXX FIXME honor safe mode - $path_delim = OS_WINDOWS ? ';' : ':'; - $exe_suffixes = OS_WINDOWS ? array('.exe','.bat','.cmd','.com') : array(''); - $path_elements = explode($path_delim, getenv('PATH')); foreach ($exe_suffixes as $suff) { foreach ($path_elements as $dir) { $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; - if (@is_file($file) && @$pear_is_executable($file)) { + if (@$pear_is_executable($file)) { return $file; } } @@ -461,43 +533,48 @@ class System } /** - * The "find" command - * - * Usage: - * - * System::find($dir); - * System::find("$dir -type d"); - * System::find("$dir -type f"); - * System::find("$dir -name *.php"); - * System::find("$dir -name *.php -name *.htm*"); - * System::find("$dir -maxdepth 1"); - * - * Params implmented: - * $dir -> Start the search at this directory - * -type d -> return only directories - * -type f -> return only files - * -maxdepth -> max depth of recursion - * -name -> search pattern (bash style). Multiple -name param allowed - * - * @param mixed Either array or string with the command line - * @return array Array of found files - * - */ + * The "find" command + * + * Usage: + * + * System::find($dir); + * System::find("$dir -type d"); + * System::find("$dir -type f"); + * System::find("$dir -name *.php"); + * System::find("$dir -name *.php -name *.htm*"); + * System::find("$dir -maxdepth 1"); + * + * Params implmented: + * $dir -> Start the search at this directory + * -type d -> return only directories + * -type f -> return only files + * -maxdepth -> max depth of recursion + * -name -> search pattern (bash style). Multiple -name param allowed + * + * @param mixed Either array or string with the command line + * @return array Array of found files + * @static + * + */ function find($args) { if (!is_array($args)) { $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); } - $dir = array_shift($args); + $dir = realpath(array_shift($args)); + if (!$dir) { + return array(); + } $patterns = array(); $depth = 0; $do_files = $do_dirs = true; - for ($i = 0; $i < count($args); $i++) { + $args_count = count($args); + for ($i = 0; $i < $args_count; $i++) { switch ($args[$i]) { case '-type': if (in_array($args[$i+1], array('d', 'f'))) { if ($args[$i+1] == 'd') { - $do_files = false; + $do_files = false; } else { $do_dirs = false; } @@ -505,10 +582,11 @@ class System $i++; break; case '-name': - $patterns[] = "(" . preg_replace(array('/\./', '/\*/'), - array('\.', '.*'), - $args[$i+1]) - . ")"; + $name = preg_quote($args[$i+1], '#'); + // our magic characters ? and * have just been escaped, + // so now we change the escaped versions to PCRE operators + $name = strtr($name, array('\?' => '.', '\*' => '.*')); + $patterns[] = '('.$name.')'; $i++; break; case '-maxdepth': @@ -516,7 +594,7 @@ class System break; } } - $path = System::_dirToStruct($dir, $depth); + $path = System::_dirToStruct($dir, $depth, 0, true); if ($do_files && $do_dirs) { $files = array_merge($path['files'], $path['dirs']); } elseif ($do_dirs) { @@ -525,10 +603,14 @@ class System $files = $path['files']; } if (count($patterns)) { - $patterns = implode('|', $patterns); + $dsq = preg_quote(DIRECTORY_SEPARATOR, '#'); + $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#'; $ret = array(); - for ($i = 0; $i < count($files); $i++) { - if (preg_match("#^$patterns\$#", $files[$i])) { + $files_count = count($files); + for ($i = 0; $i < $files_count; $i++) { + // only search in the part of the file below the current directory + $filepart = basename($files[$i]); + if (preg_match($pattern, $filepart)) { $ret[] = $files[$i]; } } @@ -536,5 +618,4 @@ class System } return $files; } -} -?> +} \ No newline at end of file diff --git a/thirdpartyjs/jquery/plugins/livequery/jquery.livequery.js b/thirdpartyjs/jquery/plugins/livequery/jquery.livequery.js new file mode 100644 index 0000000..dde8ad8 --- /dev/null +++ b/thirdpartyjs/jquery/plugins/livequery/jquery.livequery.js @@ -0,0 +1,250 @@ +/*! Copyright (c) 2008 Brandon Aaron (http://brandonaaron.net) + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * Version: 1.0.3 + * Requires jQuery 1.1.3+ + * Docs: http://docs.jquery.com/Plugins/livequery + */ + +(function($) { + +$.extend($.fn, { + livequery: function(type, fn, fn2) { + var self = this, q; + + // Handle different call patterns + if ($.isFunction(type)) + fn2 = fn, fn = type, type = undefined; + + // See if Live Query already exists + $.each( $.livequery.queries, function(i, query) { + if ( self.selector == query.selector && self.context == query.context && + type == query.type && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) ) + // Found the query, exit the each loop + return (q = query) && false; + }); + + // Create new Live Query if it wasn't found + q = q || new $.livequery(this.selector, this.context, type, fn, fn2); + + // Make sure it is running + q.stopped = false; + + // Run it immediately for the first time + q.run(); + + // Contnue the chain + return this; + }, + + expire: function(type, fn, fn2) { + var self = this; + + // Handle different call patterns + if ($.isFunction(type)) + fn2 = fn, fn = type, type = undefined; + + // Find the Live Query based on arguments and stop it + $.each( $.livequery.queries, function(i, query) { + if ( self.selector == query.selector && self.context == query.context && + (!type || type == query.type) && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) && !this.stopped ) + $.livequery.stop(query.id); + }); + + // Continue the chain + return this; + } +}); + +$.livequery = function(selector, context, type, fn, fn2) { + this.selector = selector; + this.context = context || document; + this.type = type; + this.fn = fn; + this.fn2 = fn2; + this.elements = []; + this.stopped = false; + + // The id is the index of the Live Query in $.livequery.queries + this.id = $.livequery.queries.push(this)-1; + + // Mark the functions for matching later on + fn.$lqguid = fn.$lqguid || $.livequery.guid++; + if (fn2) fn2.$lqguid = fn2.$lqguid || $.livequery.guid++; + + // Return the Live Query + return this; +}; + +$.livequery.prototype = { + stop: function() { + var query = this; + + if ( this.type ) + // Unbind all bound events + this.elements.unbind(this.type, this.fn); + else if (this.fn2) + // Call the second function for all matched elements + this.elements.each(function(i, el) { + query.fn2.apply(el); + }); + + // Clear out matched elements + this.elements = []; + + // Stop the Live Query from running until restarted + this.stopped = true; + }, + + run: function() { + // Short-circuit if stopped + if ( this.stopped ) return; + var query = this; + + var oEls = this.elements, + els = $(this.selector, this.context), + nEls = els.not(oEls); + + // Set elements to the latest set of matched elements + this.elements = els; + + if (this.type) { + // Bind events to newly matched elements + nEls.bind(this.type, this.fn); + + // Unbind events to elements no longer matched + if (oEls.length > 0) + $.each(oEls, function(i, el) { + if ( $.inArray(el, els) < 0 ) + $.event.remove(el, query.type, query.fn); + }); + } + else { + // Call the first function for newly matched elements + nEls.each(function() { + query.fn.apply(this); + }); + + // Call the second function for elements no longer matched + if ( this.fn2 && oEls.length > 0 ) + $.each(oEls, function(i, el) { + if ( $.inArray(el, els) < 0 ) + query.fn2.apply(el); + }); + } + } +}; + +$.extend($.livequery, { + guid: 0, + queries: [], + queue: [], + running: false, + timeout: null, + + checkQueue: function() { + if ( $.livequery.running && $.livequery.queue.length ) { + var length = $.livequery.queue.length; + // Run each Live Query currently in the queue + while ( length-- ) + $.livequery.queries[ $.livequery.queue.shift() ].run(); + } + }, + + pause: function() { + // Don't run anymore Live Queries until restarted + $.livequery.running = false; + }, + + play: function() { + // Restart Live Queries + $.livequery.running = true; + // Request a run of the Live Queries + $.livequery.run(); + }, + + registerPlugin: function() { + $.each( arguments, function(i,n) { + // Short-circuit if the method doesn't exist + if (!$.fn[n]) return; + + // Save a reference to the original method + var old = $.fn[n]; + + // Create a new method + $.fn[n] = function() { + // Call the original method + var r = old.apply(this, arguments); + + // Request a run of the Live Queries + $.livequery.run(); + + // Return the original methods result + return r; + } + }); + }, + + run: function(id) { + if (id != undefined) { + // Put the particular Live Query in the queue if it doesn't already exist + if ( $.inArray(id, $.livequery.queue) < 0 ) + $.livequery.queue.push( id ); + } + else + // Put each Live Query in the queue if it doesn't already exist + $.each( $.livequery.queries, function(id) { + if ( $.inArray(id, $.livequery.queue) < 0 ) + $.livequery.queue.push( id ); + }); + + // Clear timeout if it already exists + if ($.livequery.timeout) clearTimeout($.livequery.timeout); + // Create a timeout to check the queue and actually run the Live Queries + $.livequery.timeout = setTimeout($.livequery.checkQueue, 20); + }, + + stop: function(id) { + if (id != undefined) + // Stop are particular Live Query + $.livequery.queries[ id ].stop(); + else + // Stop all Live Queries + $.each( $.livequery.queries, function(id) { + $.livequery.queries[ id ].stop(); + }); + } +}); + +// Register core DOM manipulation methods +$.livequery.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove'); + +// Run Live Queries when the Document is ready +$(function() { $.livequery.play(); }); + + +// Save a reference to the original init method +var init = $.prototype.init; + +// Create a new init method that exposes two new properties: selector and context +$.prototype.init = function(a,c) { + // Call the original init and save the result + var r = init.apply(this, arguments); + + // Copy over properties if they exist already + if (a && a.selector) + r.context = a.context, r.selector = a.selector; + + // Set properties + if ( typeof a == 'string' ) + r.context = c || document, r.selector = a; + + // Return the result + return r; +}; + +// Give the init function the jQuery prototype for later instantiation (needed after Rev 4091) +$.prototype.init.prototype = $.prototype; + +})(jQuery); \ No newline at end of file diff --git a/thirdpartyjs/jquery/plugins/selectimage/css/selectimage.css b/thirdpartyjs/jquery/plugins/selectimage/css/selectimage.css new file mode 100644 index 0000000..b9b18fd --- /dev/null +++ b/thirdpartyjs/jquery/plugins/selectimage/css/selectimage.css @@ -0,0 +1,31 @@ +#selectimage_container img { + cursor: pointer; + text-decoration: none; + border: 10px solid white; +} + +#selectimage_container img:hover { + text-decoration: none; + border: 10px solid white; +} + +.jq_select_image { + float: left; + border: 1px solid #cccccc; + margin-right: 5px; + padding: 0px; + display:inline; + height:70px; +} + +.selectimage_border_background { + border: 10px solid white; +} + +.selectimage_border_hover { + border: 10px solid #9c9c9c; +} + +.selectimage_border_click { + border: 10px solid #f2943a; +} \ No newline at end of file diff --git a/thirdpartyjs/jquery/plugins/selectimage/jquery.selectimage.js b/thirdpartyjs/jquery/plugins/selectimage/jquery.selectimage.js new file mode 100755 index 0000000..88f7c0e --- /dev/null +++ b/thirdpartyjs/jquery/plugins/selectimage/jquery.selectimage.js @@ -0,0 +1,100 @@ +/** + * jQuery Select Image Plugin + * + * @author Charl Mert + * + * KnowledgeTree Community Edition + * Document Management Made Simple + * Copyright (C) 2009, 2010 KnowledgeTree Inc. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * California 94120-7775, or email info@knowledgetree.com. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "Powered by + * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices + * must display the words "Powered by KnowledgeTree" and retain the original + * copyright notice. + * Contributor( s): ______________________________________ + */ + +jQuery.fn.selectImage = function() { + return this.each(function(){ + + var opt = jQuery.extend({ + passBackNamespace : 'selectimage', //TODO: Let plugin detect parent form and produce the hidden inputs. For now the user has to place the predefined hidden inputs onto the form manually. + multiselect : false, + hover : '#e2e2e2', + click : '#f2943a', //Orange + background : 'white', + selected : '' + }, opt); + + var containerId = this.id; + var custId = 0; + + //Setting up border + jQuery('#' + containerId + ' img' ).css('border', '10px solid ' + opt.background); + + jQuery('#' + containerId + ' img').each(function(){ + //Assigning custom id's to attach event handlers to + if (this.id == '') { + this.id = 'select_image_img_' + custId; + custId++; + } + + var toggle = 1; + + //Click to select + jQuery('#' + this.id).click(function(){ + opt.selected = this.id; + jQuery('#' + opt.passBackNamespace + '_src').val(this.src); + jQuery('#' + opt.passBackNamespace + '_alt').val(this.alt); + jQuery('#' + opt.passBackNamespace + '_title').val(this.title); + + //Resetting background: + jQuery('#' + containerId + ' img' ).css('border', '10px solid ' + opt.background); + jQuery('#' + this.id).css('border', '10px solid ' + opt.click); + }) + + //Hover Effect + jQuery('#' + this.id).mouseenter(function(){ + if (this.id != opt.selected) { + jQuery('#' + this.id).css('border', '10px solid ' + opt.hover).show("slow"); + } + }) + + jQuery('#' + this.id).mouseleave(function(){ + if (this.id != opt.selected) { + jQuery('#' + this.id).css('border', '10px solid ' + opt.background).show("slow"); + } + }) + + }); + }); +}; + +jQuery.log = function(message) { + if(window.console) { + console.debug(message); + } else { + alert(message); + } +}; \ No newline at end of file diff --git a/view.php b/view.php index 897cb4d..ea9b2ef 100644 --- a/view.php +++ b/view.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the diff --git a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php index 6f56e6c..bce4c4e 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php @@ -31,15 +31,12 @@ When POSTing an Atom Document, the atom fields take precedence over the CMIS pro */ // load all available CMIS services -include_once CMIS_ATOM_LIB_FOLDER . 'RepositoryService.inc.php'; -include_once CMIS_ATOM_LIB_FOLDER . 'NavigationService.inc.php'; -include_once CMIS_ATOM_LIB_FOLDER . 'ObjectService.inc.php'; -include_once CMIS_ATOM_LIB_FOLDER . 'VersioningService.inc.php'; +include_once CMIS_API . '/ktRepositoryService.inc.php'; +include_once CMIS_API . '/ktNavigationService.inc.php'; +include_once CMIS_API . '/ktObjectService.inc.php'; +include_once CMIS_API . '/ktVersioningService.inc.php'; include_once 'KT_cmis_atom_service_helper.inc.php'; -// TODO consider changing all responses from the webservice layer to return PEAR errors or success results instead of the half/half we have at the moment. -// the half/half occurred because on initial services PEAR Error seemed unnecessary, but it has proven useful for some of the newer functions - // TODO proper first/last links // FIXME any incorrect or missing links // FIXME ContentStreamAllowed tag is empty (at least sometimes) @@ -54,7 +51,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { * This includes children and tree/descendant listings as well as individual folder retrieval */ public function GET_action() - { + { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); // TODO implement full path/node separation as with Alfresco - i.e. path requests come in on path/ and node requests come in on node/ @@ -75,37 +72,41 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { else if (($this->params[1] == 'children') || ($this->params[1] == 'descendants')) { $folderId = $this->params[0]; - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); $response = $ObjectService->getProperties($repositoryId, $folderId, false, false); - if (PEAR::isError($response)) { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response['message']); $this->responseFeed = $feed; return null; } + else { + $response = $response['results']; + } - $folderName = $response['properties']['Name']['value']; + $folderName = $response['properties']['name']['value']; } // NOTE parent changes to parents in later specification // TODO update when updating to later specification // TODO this only returns one parent, need to implement returnToRoot also else if ($this->params[1] == 'parent') { - // abstract this to be used also by the document service (and the PWC service?) ??? - // alternatively use getFolderParent here makes sense and use getObjectParents when document service? $folderId = $this->params[0]; - $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); + $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); $response = $NavigationService->getFolderParent($repositoryId, $folderId, false, false, false); - if (PEAR::isError($response)) { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response['message']); $this->responseFeed = $feed; return null; } + else { + $response = $response['results']; + } // we know that a folder will only have one parent, so we can assume element 0 - $folderId = $response[0]['properties']['ObjectId']['value']; - $folderName = $response[0]['properties']['Name']['value']; + $folderId = $response['properties']['objectId']['value']; + $folderName = $response['properties']['name']['value']; } else { $folderId = $this->params[0]; @@ -113,12 +114,12 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { if (!empty($this->params[1]) && (($this->params[1] == 'children') || ($this->params[1] == 'descendants'))) { - $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); + $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); $feed = $this->getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $this->params[1]); } else { - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $folderId); } @@ -131,7 +132,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { * This includes creation/moving of both folders and documents. */ public function POST_action() - { + { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); // set default action, objectId and typeId @@ -140,32 +141,31 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { $typeId = null; $folderId = $this->params[0]; - $title = KT_cmis_atom_service_helper::getAtomValues($this->parsedXMLContent['@children'], 'title'); - $summary = KT_cmis_atom_service_helper::getAtomValues($this->parsedXMLContent['@children'], 'summary'); - - $properties = array('name' => $title, 'summary' => $summary); + $title = KT_cmis_atom_service_helper::getAtomValues($this->rawContent, 'title'); + $summary = KT_cmis_atom_service_helper::getAtomValues($this->rawContent, 'summary'); // determine whether this is a folder or a document action // document action create will have a content tag or containing base64 encoding of the document // move action will have an existing id supplied as a parameter - not sure how this works yet as the CMIS clients we are // testing don't support move functionality at this time (2009/07/23) and so we are presuming the following format: - // /folder//children/ + // /folder/// // also possible that there will be an existing ObjectId property, try to cater for both until we know how it really works + // NOTE this also applies to the source folder id, see above // check for existing object id as parameter in url - if (isset($this->params[2])) - { + // if sourceFolderId parameter is submitted (expected as parameter 3, or params[2]) then this is a move + if (isset($this->params[2])) { $action = 'move'; - $objectId = $this->params[2]; + $sourceFolderId = $this->params[2]; } - $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisProperties($this->parsedXMLContent['@children']); + // get object properties - todo send through original properties array and not modified version + $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisObjectProperties($this->rawContent); + $properties = array('name' => $title, 'summary' => $summary, 'objectTypeId' => $cmisObjectProperties['cmis:objectTypeId']); // check for existing object id as property of submitted object data - if (!empty($cmisObjectProperties['ObjectId'])) - { - $action = 'move'; - $objectId = $cmisObjectProperties['ObjectId']; + if (!empty($cmisObjectProperties['cmis:objectId'])) { + $objectId = $cmisObjectProperties['cmis:objectId']; } // TODO there may be more to do for the checking of an existing object. @@ -179,42 +179,59 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { } // check for content stream - $content = KT_cmis_atom_service_helper::getAtomValues($this->parsedXMLContent['@children'], 'content'); - - // TODO this will possibly need to change somewhat once Relationship Objects come into play. - if ((($action == 'create') && (is_null($content))) || ($typeId == 'Folder')) { - $type = 'folder'; - } - else { - $type = 'document'; - } + $content = KT_cmis_atom_service_helper::getCmisContent($this->rawContent); + // NOTE not sure about the text type, will need testing, most content will be base64 + $cmisContent = (isset($content['cmisra:base64']) + ? $content['cmisra:base64'] + : ((isset($content['cmisra:text'])) + ? $content['cmisra:text'] + : null)); - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); $success = false; $error = null; if ($action == 'create') { - if ($type == 'folder') - $newObjectId = $ObjectService->createFolder($repositoryId, ucwords($cmisObjectProperties['ObjectTypeId']), $properties, $folderId); - else - $newObjectId = $ObjectService->createDocument($repositoryId, ucwords($cmisObjectProperties['ObjectTypeId']), $properties, $folderId, $content); + // TODO detection and passing of optional parameters (policies, ACEs, etc...) as well as support for other object-types + if ($cmisObjectProperties['cmis:objectTypeId'] == 'cmis:folder') { + $newObjectId = $ObjectService->createFolder($repositoryId, $properties, $folderId); + } + else { + // NOTE for the moment only creation in minor versioning state + $newObjectId = $ObjectService->createDocument($repositoryId, $properties, $folderId, $cmisContent, 'minor'); + } - // check if returned Object Id is a valid CMIS Object Id - CMISUtil::decodeObjectId($newObjectId, $typeId); - if ($typeId != 'Unknown') $success = true; - else $error = $newObjectId['message']; + if ($newObjectId['status_code'] == 0) { + $newObjectId = $newObjectId['results']; + // check if returned Object Id is a valid CMIS Object Id + CMISUtil::decodeObjectId($newObjectId, $typeId); + if ($typeId != 'unknown') { + $success = true; + } + else { + $error = 'Unknown Object Type'; + } + } + else { + $error = $newObjectId['message']; + } } else if ($action == 'move') { - $response = $ObjectService->moveObject($repositoryId, $objectId, '', $folderId); + $response = $ObjectService->moveObject($repositoryId, $objectId, $folderId, $sourceFolderId); - if (!PEAR::isError($response)) $success = true; - else $error = $response->getMessage(); + if ($response['status_code'] == 0) { + $success = true; + } + else { + $error = $response['message']; + } // same object as before $newObjectId = $objectId; - $typeId = ucwords($type); + // FIXME why set this? it does not appear to get used + $typeId = ucwords($cmisObjectProperties['cmis:objectTypeId']); } if ($success) @@ -241,22 +258,25 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { // NOTE due to the way KnowledgeTree works with folders this is always going to call deleteTree. // we COULD call deleteObject but when we delete a folder we expect to be trying to delete // the folder and all content. + // TODO determine whether client is requesting deleteObject or deleteTree $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); - // attempt delete - $response = $ObjectService->deleteTree($repositoryId, $this->params[0]); + // attempt delete - last parameter sets $deleteAllVersions true + $response = $ObjectService->deleteTree($repositoryId, $this->params[0], 'delete', true); // error? - if (PEAR::isError($response)) - { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response['message']); // Expose the responseFeed $this->responseFeed = $feed; return null; } + else { + $response = $response['results']; + } // list of failed objects? if (is_array($response)) @@ -269,15 +289,14 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { foreach($response as $failed) { $entry = $feed->newEntry(); - $objectElement = $feed->newElement('cmis:object'); + $objectElement = $feed->newElement('cmisra:object'); $propertiesElement = $feed->newElement('cmis:properties'); $propElement = $feed->newElement('cmis:propertyId'); - $propElement->appendChild($feed->newAttr('cmis:name', 'ObjectId')); + $propElement->appendChild($feed->newAttr('cmis:name', 'objectId')); $feed->newField('cmis:value', $failed, $propElement); $propertiesElement->appendChild($propElement); $objectElement->appendChild($propertiesElement); $entry->appendChild($objectElement); - $entry->appendChild($feed->newElement('cmis:terminator')); } $this->responseFeed = $feed; @@ -303,11 +322,21 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false); } else if ($feedType == 'descendants') { - $entries = $NavigationService->getDescendants($repositoryId, $folderId, false, false); + // TODO how will client request depth? for now we assume as part of the url - will probably be covered by URI templates + if (isset($this->params[2])) { + $entries = $NavigationService->getDescendants($repositoryId, $folderId, $this->params[2]); + + } + else { + $entries = $NavigationService->getDescendants($repositoryId, $folderId); + } } else { // error, we shouldn't be here, if we are then the wrong service/function was called } + + // hack, for removing one level of access + $entries = $entries['results']; // $baseURI=NULL,$title=NULL,$link=NULL,$updated=NULL,$author=NULL,$id=NULL $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); @@ -330,19 +359,18 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { $link->appendChild($feed->newAttr('rel', 'self')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId . '/' . $feedType)); $feed->appendChild($link); + + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'service')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . 'servicedocument')); + $feed->appendChild($link); $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel', 'source')); + $link->appendChild($feed->newAttr('rel', 'via')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' . $folderId)); $feed->appendChild($link); - foreach($entries as $cmisEntry) - { - KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $folderName); - - // after each entry, add app:edited tag - $feed->newField('app:edited', KT_cmis_atom_service_helper::formatDatestamp(), $feed); - } + KT_cmis_atom_service_helper::createObjectFeed($feed, $entries, $folderName); $feed->newField('cmis:hasMoreItems', 'false', $feed); @@ -365,7 +393,7 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service { { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); $objectId = $this->params[0]; @@ -373,20 +401,21 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service { // update accordingly when updating to newer specification if ($this->params[1] == 'parent') { - // abstract this to be used also by the document service (and the PWC service?) ??? - // alternatively use getFolderParent here makes sense and use getObjectParents when document service? - $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); + $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); $response = $NavigationService->getObjectParents($repositoryId, $objectId, false, false); - if (PEAR::isError($response)) { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response['message']); $this->responseFeed = $feed; return null; } + else { + $response = $response['results']; + } // for now a document will only have one parent as KnowledgeTree does not support multi-filing // TODO update this code if/when multi-filing support is added - $objectId = $response[0]['properties']['ObjectId']['value']; + $objectId = $response[0]['properties']['objectId']['value']; } // determine whether we want the document entry feed or the actual physical document content. // this depends on $this->params[1] @@ -410,21 +439,17 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service { * @return 204 on success, 500 on error */ public function DELETE_action() - { - // NOTE due to the way KnowledgeTree works with documents this is always going to call deleteAllVersions. - // we do not have support for deleting only specific versions (this may be added in the future.) - + { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); - $VersioningService = new VersioningService(KT_cmis_atom_service_helper::getKt()); - + $VersioningService = new KTVersioningService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); + // attempt delete - $response = $VersioningService->deleteAllVersions($repositoryId, $this->params[0]); + $response = $ObjectService->deleteObject($repositoryId, $this->params[0]); - // error? - if (PEAR::isError($response)) - { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response['message']); // Expose the responseFeed $this->responseFeed = $feed; return null; @@ -448,7 +473,7 @@ class KT_cmis_atom_service_pwc extends KT_cmis_atom_service { { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); // determine whether we want the Private Working Copy entry feed or the actual physical Private Working Copy content. // this depends on $this->params[1] @@ -475,15 +500,13 @@ class KT_cmis_atom_service_pwc extends KT_cmis_atom_service { { // call the cancel checkout function - $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); + $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); + $VersioningService = new KTVersioningService(KT_cmis_atom_service_helper::getKt()); - $VersioningService = new VersioningService(KT_cmis_atom_service_helper::getKt()); - $response = $VersioningService->cancelCheckout($repositoryId, $this->params[0]); - if (PEAR::isError($response)) - { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response['message']); // Expose the responseFeed $this->responseFeed = $feed; return null; @@ -496,45 +519,41 @@ class KT_cmis_atom_service_pwc extends KT_cmis_atom_service { public function PUT_action() { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); + $VersioningService = new KTVersioningService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); - $VersioningService = new VersioningService(KT_cmis_atom_service_helper::getKt()); - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + // get object properties + $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisObjectProperties($this->rawContent); // check for content stream - // NOTE this is a hack! will not work with CMISSpaces at least, probably not with any client except RestTest and similar - // where we can manually modify the input - // first we try for an atom content tag - $content = KT_cmis_atom_service_helper::getAtomValues($this->parsedXMLContent['@children'], 'content'); - if (!empty($content)) { - $contentStream = $content; - } - // not found? try for a regular content tag - else { - $content = KT_cmis_atom_service_helper::findTag('content', $this->parsedXMLContent['@children'], null, false); - $contentStream = $content['@value']; - } - - // if we haven't found it now, the real hack begins - retrieve the EXISTING content and submit this as the contentStream + $content = KT_cmis_atom_service_helper::getCmisContent($this->rawContent); + // NOTE not sure about the text type, will need testing, most content will be base64 + $cmisContent = (isset($content['cmisra:base64']) + ? $content['cmisra:base64'] + : ((isset($content['cmisra:text'])) + ? $content['cmisra:text'] + : null)); + + // if we haven't found it now, the hack begins - retrieve the EXISTING content and submit this as the contentStream // this is needed because KnowledgeTree will not accept a checkin without a content stream but CMISSpaces (and possibly // other CMIS clients are the same, does not send a content stream on checkin nor does it offer the user a method to choose one) // NOTE that if the content is INTENDED to be empty this and all the above checks will FAIL! // FIXME this is horrible, terrible, ugly and bad! - if (empty($contentStream)) { - $contentStream = base64_encode(KT_cmis_atom_service_helper::getContentStream($this, $ObjectService, $repositoryId)); + if (empty($cmisContent)) { + $cmisContent = base64_encode(KT_cmis_atom_service_helper::getContentStream($this, $ObjectService, $repositoryId)); } - // and if we don't have it by now, we give up...but leave the error to be generated by the underlying KnowledgeTree code - + // and if we don't have the content stream by now, we give up...but leave the error to be generated by the underlying KnowledgeTree code // checkin function call // TODO dynamically detect version change type - leaving this for now as the CMIS clients tested do not appear to // offer the choice to the user - perhaps it will turn out that this will come from somewhere else but for now // we assume minor version updates only $major = false; - $response = $VersioningService->checkIn($repositoryId, $this->params[0], $major, $contentStream); - - if (PEAR::isError($response)) - { - $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response->getMessage()); + $checkinComment = ''; + $response = $VersioningService->checkIn($repositoryId, $this->params[0], $major, $cmisObjectProperties, $cmisContent, $checkinComment); + + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response['message']); // Expose the responseFeed $this->responseFeed = $feed; return null; @@ -559,10 +578,12 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { public function GET_action() { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); - - $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); + $NavigationService = new KTNavigationService(KT_cmis_atom_service_helper::getKt()); $checkedout = $NavigationService->getCheckedOutDocs($repositoryId); + + // hack, for removing one level of access + $checkedout = $checkedout['results']; //Create a new response feed $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); @@ -598,12 +619,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed')); $feed->appendChild($link); - foreach($checkedout as $cmisEntry) - { + foreach($checkedout as $cmisEntry) { KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $folderName, true); - -// // after each entry, add app:edited tag -// $feed->newField('app:edited', KT_cmis_atom_service_helper::formatDatestamp(), $feed); } $feed->newField('cmis:hasMoreItems', 'false', $feed); @@ -615,14 +632,13 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { public function POST_action() { $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); - - $VersioningService = new VersioningService(KT_cmis_atom_service_helper::getKt()); - $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt()); + $VersioningService = new KTVersioningService(KT_cmis_atom_service_helper::getKt()); + $ObjectService = new KTObjectService(KT_cmis_atom_service_helper::getKt()); - $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisProperties($this->parsedXMLContent['@children']); + $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisObjectProperties($this->rawContent); // check for existing object id as property of submitted object data - if (empty($cmisObjectProperties['ObjectId'])) + if (empty($cmisObjectProperties['cmis:objectId'])) { $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, 'No object was specified for checkout'); // Expose the responseFeed @@ -630,10 +646,9 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { return null; } - $response = $VersioningService->checkOut($repositoryId, $cmisObjectProperties['ObjectId']); + $response = $VersioningService->checkOut($repositoryId, $cmisObjectProperties['cmis:objectId']); - if (PEAR::isError($response)) - { + if ($response['status_code'] == 1) { $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, 'No object was specified for checkout'); // Expose the responseFeed $this->responseFeed = $feed; @@ -641,7 +656,7 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { } $this->setStatus(self::STATUS_CREATED); - $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['ObjectId'], 'POST'); + $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['cmis:objectId'], 'POST'); // Expose the responseFeed $this->responseFeed = $feed; @@ -656,10 +671,14 @@ class KT_cmis_atom_service_types extends KT_cmis_atom_service { public function GET_action() { - $RepositoryService = new RepositoryService(); + $RepositoryService = new KTRepositoryService(); $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); $types = $RepositoryService->getTypes($repositoryId); + + // hack for removing one level of access + $types = $types['results']; + $type = ((empty($this->params[0])) ? 'all' : $this->params[0]); $feed = KT_cmis_atom_service_helper::getTypeFeed($type, $types); @@ -676,26 +695,22 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { public function GET_action() { - $RepositoryService = new RepositoryService(); + $RepositoryService = new KTRepositoryService(); $repositoryId = KT_cmis_atom_service_helper::getRepositoryId($RepositoryService); + $type = $this->params[0]; - if (!isset($this->params[1])) { - // For easier return in the wanted format, we call getTypes instead of getTypeDefinition. - // Calling this with a single type specified returns an array containing the definition of - // just the requested type. - // NOTE could maybe be more efficient to call getTypeDefinition direct and then place in - // an array on this side? or directly expose the individual entry response code and - // call directly from here rather than via getTypeFeed. - $type = ucwords($this->params[0]); - $types = $RepositoryService->getTypes($repositoryId, $type); - $feed = KT_cmis_atom_service_helper::getTypeFeed($type, $types); + try { + $typeDefinition = $RepositoryService->getTypeDefinition($repositoryId, $type); } - else { - // TODO dynamic dates, as needed everywhere - // NOTE children of types not yet implemented and we don't support any non-basic types at this time - $feed = $this->getTypeChildrenFeed($this->params[1]); + catch (Exception $e) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $e->getMessage()); + // Expose the responseFeed + $this->responseFeed = $feed; + return null; } + $feed = KT_cmis_atom_service_helper::getTypeFeed($type, array($typeDefinition['attributes'])); + // Expose the responseFeed $this->responseFeed=$feed; } @@ -712,7 +727,6 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { private function getTypeChildrenFeed() { //Create a new response feed - // $baseURI=NULL,$title=NULL,$link=NULL,$updated=NULL,$author=NULL,$id=NULL $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); $feed->newField('title', 'Child Types of ' . ucwords($this->params[0]), $feed); @@ -740,4 +754,4 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { } -?> \ No newline at end of file +?> diff --git a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php index 3746bfd..03a12f4 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php @@ -16,11 +16,14 @@ class KT_cmis_atom_service_helper { static public function setRepositoryId(&$RepositoryService = null) { if (is_null($RepositoryService)) { - $RepositoryService = new RepositoryService(); + $RepositoryService = new KTRepositoryService(); } $repositories = $RepositoryService->getRepositories(); + // hack for removing one level of access + $repositories = $repositories['results']; + // TODO handle multiple repositories self::$repositoryId = $repositories[0]['repositoryId']; } @@ -53,6 +56,7 @@ class KT_cmis_atom_service_helper { * @param string $folderId * @return string CMIS AtomPub feed */ + // TODO enable this to work on an existing set of object properties if submitted static public function getObjectFeed(&$service, $ObjectService, $repositoryId, $objectId, $method = 'GET') { self::$repositoryId = $repositoryId; @@ -60,11 +64,11 @@ class KT_cmis_atom_service_helper { $serviceType = $service->getServiceType(); $response = $ObjectService->getProperties($repositoryId, $objectId, false, false); - if (PEAR::isError($response)) { - return KT_cmis_atom_service_helper::getErrorFeed($service, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + return KT_cmis_atom_service_helper::getErrorFeed($service, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response['message']); } - - $cmisEntry = $response; + + $cmisEntry = $response['results']; $response = null; // POST/PWC responses only send back an entry, not a feed @@ -78,253 +82,313 @@ class KT_cmis_atom_service_helper { } else if ($method == 'GET') { $response = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); - $response->newField('title', $cmisEntry['properties']['ObjectTypeId']['value'], $response); - $response->newField('id', 'urn:uuid:' . $cmisEntry['properties']['ObjectId']['value'], $response); + $response->newField('title', $cmisEntry['properties']['objectTypeId']['value'], $response); + $response->newField('id', 'urn:uuid:' . $cmisEntry['properties']['objectId']['value'], $response); } if ($serviceType == 'PWC') $pwc = true; else $pwc = false; - KT_cmis_atom_service_helper::createObjectEntry($response, $cmisEntry, $cmisEntry['properties']['ParentId']['value'], $pwc, $method); - - // Don't think this should be here...only one item so why would we need to say there are no more? - /*if ($method == 'GET') { - $response->newField('cmis:hasMoreItems', 'false', $response); - }*/ + KT_cmis_atom_service_helper::createObjectEntry($response, $cmisEntry, $cmisEntry['properties']['parentId']['value'], $pwc, $method); return $response; } + + static public function createObjectFeed(&$feed, $entries, $folderName) + { + foreach($entries as $cmisEntry) { + self::createObjectEntry($feed, $cmisEntry, $folderName); + } + } /** * Creates an AtomPub entry for a CMIS entry and adds it to the supplied feed * * @param object $feed The feed to which we add the entry * @param array $cmisEntry The entry data - * @param string $parent The parent folder + * @param string $parent The parent folder - this appears to be unused, not sure what it was meant for + * @param boolean $pwc Whether this is a PWC object + * @param $method The request method used (POST/GET/...) */ - static public function createObjectEntry(&$response, $cmisEntry, $parent, $pwc = false, $method = 'GET') + static public function createObjectEntry(&$feed, $cmisEntry, $parent, $pwc = false, $method = 'GET') { - $workspace = $response->getWorkspace(); - $type = strtolower($cmisEntry['properties']['ObjectTypeId']['value']); + $workspace = $feed->getWorkspace(); // create entry - $entry = $response->newEntry(); + $entry = $feed->newEntry(); - // FIXME this maybe belongs in the response feed class only how? + // When request is a POST we will be returning only an object entry, not a full feed, and so this belongs here if (($method == 'POST') || $pwc) { // append attributes - $entry->appendChild($response->newAttr('xmlns', 'http://www.w3.org/2005/Atom')); - $entry->appendChild($response->newAttr('xmlns:app', 'http://www.w3.org/2007/app')); - $entry->appendChild($response->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200901')); + $entry->appendChild($feed->newAttr('xmlns', 'http://www.w3.org/2005/Atom')); + $entry->appendChild($feed->newAttr('xmlns:app', 'http://www.w3.org/2007/app')); + $entry->appendChild($feed->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200908/')); + $entry->appendChild($feed->newAttr('xmlns:cmisra', 'http://docs.oasis-open.org/ns/cmis/restatom/200908/')); } + self::createObjectEntryContent($entry, $feed, $workspace, $cmisEntry, $parent, $pwc, $method); + } + + /** + * Creates an AtomPub child feed for a CMIS entry and adds it to the supplied entry + * + * @param $feed The child feed element + * @param $entries Entried contained within the child feed element + * @param $folderName The parent folder name (currently unused) + */ + static public function createObjectChildrenFeed(&$childrenFeed, $entries, $workspace, $feed, $folderName) + { + foreach($entries as $cmisEntry) { + self::createChildObjectEntry($childrenFeed, $cmisEntry, $workspace, $feed, $folderName); + } + } + + /** + * Creates an AtomPub child feed for a CMIS entry and adds it to the supplied entry + * + * @param object $entry The entry to which we add the child feed + * @param array $cmisEntry The object entry data + */ + // NOTE this approach appears to be necessary due to the structure of the underlying atompub code and specification, + // which does not directly support nesting - attempting to create a new feed and append it within the outer + // feed resulted in an empty cmisra:children node, so this approach was substituted + static public function createChildObjectEntry(&$childrenFeed, $cmisEntry, $workspace, $feed, $folderNam) + { + $type = strtolower($cmisEntry['properties']['objectTypeId']['value']); + + // create entry + $entry = $feed->newElement('entry'); + self::createObjectEntryContent($entry, $feed, $workspace, $cmisEntry);//, $parent, $pwc, $method); + $childrenFeed->appendChild($entry); + } + + /** + * Creates the actual object entry: this is shared between other functions which require this content + * + * @param object $entry The entry object + * @param object $feed The response feed + * @param array $cmisEntry The CMIS object content + * @param string $parent The parent folder name + * @param boolean $pwc Whether this is a PWC object (will be returned slightly differently) + * @param string $method The calling method (slightly affects the output) + */ + static public function createObjectEntryContent($entry, &$feed, $workspace, $cmisEntry, $parent = '', $pwc = false, $method = 'GET') + { + $type = $cmisEntry['properties']['objectTypeId']['value']; + // TODO dynamic actual creator name - $responseElement = $response->newField('author'); - $element = $response->newField('name', 'admin', $responseElement); + $responseElement = $feed->newField('author'); + $element = $feed->newField('name', 'admin', $responseElement); $entry->appendChild($responseElement); - if (!empty($cmisEntry['properties']['ContentStreamLength']['value'])) + $typeString = str_replace('cmis:', '', $type); + + if (!empty($cmisEntry['properties']['contentStreamLength']['value'])) { - $field = $response->newElement('content'); - $field->appendChild($response->newAttr('type', $cmisEntry['properties']['ContentStreamMimeType']['value'])); - $field->appendChild($response->newAttr('src', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); + $field = $feed->newElement('content'); + $field->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $field->appendChild($feed->newAttr('src', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($field); } // content & id tags - $id = $cmisEntry['properties']['ObjectId']['value']; - - $response->newField('id', 'urn:uuid:' . $id, $entry); + $id = $cmisEntry['properties']['objectId']['value']; + $feed->newField('id', 'urn:uuid:' . $id, $entry); // links - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'self')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . (!$pwc ? $type : 'pwc') . '/' - . $cmisEntry['properties']['ObjectId']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'self')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' + . (!$pwc ? $typeString : 'pwc') . '/' + . $cmisEntry['properties']['objectId']['value'])); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'edit')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'edit')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + . '/' . $cmisEntry['properties']['objectId']['value'])); $entry->appendChild($link); - if ((strtolower($cmisEntry['properties']['ObjectTypeId']['value']) == 'document') - && (!empty($cmisEntry['properties']['ContentStreamLength']['value']))) + if ((strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document') + && (!empty($cmisEntry['properties']['contentStreamLength']['value']))) { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'edit-media')); - $link->appendChild($response->newAttr('type', $cmisEntry['properties']['ContentStreamMimeType']['value'])); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'edit-media')); + $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'enclosure')); - $link->appendChild($response->newAttr('type', $cmisEntry['properties']['ContentStreamMimeType']['value'])); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'enclosure')); + $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); } // according to spec this MUST be present, but spec says that links for function which are not supported // do not need to be present, so unsure for the moment - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'allowableactions')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' - . $cmisEntry['properties']['ObjectId']['value'] . '/permissions')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' + . $cmisEntry['properties']['objectId']['value'] . '/allowableactions')); $entry->appendChild($link); // according to spec this MUST be present, but spec says that links for function which are not supported // do not need to be present, so unsure for the moment - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'relationships')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' - . $cmisEntry['properties']['ObjectId']['value'] . '/rels')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'http://docs.oasis-open.org/ns/cmis/link/200908/relationships')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $typeString . '/' + . $cmisEntry['properties']['objectId']['value'] . '/rels')); $entry->appendChild($link); // if there is no parent or parent is 0, do not add the parent link // also if this is specifically the root folder, do not add the parent link -// if (!empty($cmisEntry['properties']['ParentId']['value']) && !CMISUtil::isRootFolder(self::$repositoryId, $cmisEntry['properties']['ObjectId']['value'])) +// if (!empty($cmisEntry['properties']['parentId']['value']) && !CMISUtil::isRootFolder(self::$repositoryId, $cmisEntry['properties']['objectId']['value'])) - if (!CMISUtil::isRootFolder(self::$repositoryId, $cmisEntry['properties']['ObjectId']['value'], self::$ktapi)) + if (!CMISUtil::isRootFolder(self::$repositoryId, $cmisEntry['properties']['objectId']['value'], self::$ktapi)) { // TODO check parent link is correct, fix if needed - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'parents')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' - . $cmisEntry['properties']['ObjectId']['value'] . '/parent')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'up')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/folder/' + . $cmisEntry['properties']['parentId']['value'])); $entry->appendChild($link); } // Folder/Document specific links - if (strtolower($cmisEntry['properties']['ObjectTypeId']['value']) == 'folder') + if (strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:folder') { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'children')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' - . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/children')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'down')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' + . $typeString + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/children')); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'descendants')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' - . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/descendants')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'down')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' + . $typeString + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/descendants')); $entry->appendChild($link); + + // TODO add folder tree link when we have folder tree implemented + // this will probably use (much) the same code as the folder children functionality } - else if (strtolower($cmisEntry['properties']['ObjectTypeId']['value']) == 'document') + else if (strtolower($cmisEntry['properties']['objectTypeId']['value']) == 'cmis:document') { - // according to spec this MUST be present, but spec says that links for function which are not supported - // do not need to be present, so unsure for the moment -// $link = $response->newElement('link'); -// $link->appendChild($response->newAttr('rel', 'allversions')); -// $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); -// $entry->appendChild($link); - - // according to spec this MUST be present, but spec says that links for function which are not supported - // do not need to be present, so unsure for the moment -// $link = $response->newElement('link'); -// $link->appendChild($response->newAttr('rel', 'latestversion')); -// $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); -// $entry->appendChild($link); - // if there is a content stream, this link MUST be present // not sure yet where it must point... - if (!empty($cmisEntry['properties']['ContentStreamLength']['value'])) + if (!empty($cmisEntry['properties']['contentStreamLength']['value'])) { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'stream')); - $link->appendChild($response->newAttr('type', $cmisEntry['properties']['ContentStreamMimeType']['value'])); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'stream')); + $link->appendChild($feed->newAttr('type', $cmisEntry['properties']['contentStreamMimeType']['value'])); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); } // if the document is checked out and this is NOT the PWC, this link MUST be present // NOTE at the moment the document and the PWC are the same object, so we always show it for a checked out document // TODO separated code for PWC and actual document object - if (!empty($cmisEntry['properties']['VersionSeriesCheckedOutId']['value'])) + if (!empty($cmisEntry['properties']['versionSeriesCheckedOutId']['value'])) { - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'pwc')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'pwc')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'source')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type - . '/' . $cmisEntry['properties']['ObjectId']['value'] - . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'source')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type + . '/' . $cmisEntry['properties']['objectId']['value'] + . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); $entry->appendChild($link); } -// $link = $response->newElement('link'); -// $link->appendChild($response->newAttr('rel', 'stream')); -// $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type -// . '/' . $cmisEntry['properties']['ObjectId']['value'] -// . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); +// $link = $feed->newElement('link'); +// $link->appendChild($feed->newAttr('rel', 'stream')); +// $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type +// . '/' . $cmisEntry['properties']['objectId']['value'] +// . '/' . $cmisEntry['properties']['contentStreamFilename']['value'])); } - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'type')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'describedby')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . $type)); $entry->appendChild($link); - $link = $response->newElement('link'); - $link->appendChild($response->newAttr('rel', 'repository')); - $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); + $link = $feed->newElement('link'); + $link->appendChild($feed->newAttr('rel', 'service')); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); - // according to spec this MUST be present, but spec says that links for function which are not supported - // do not need to be present, so unsure for the moment - policies are being abandoned, or so I thought... -// $link = $response->newElement('link'); -// $link->appendChild($response->newAttr('rel', 'policies')); -// $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value'])); -// $entry->appendChild($link); - // end links - // TODO proper date - $entry->appendChild($response->newField('published', self::formatDatestamp())); - $entry->appendChild($response->newElement('summary', $cmisEntry['properties']['Name']['value'])); - $entry->appendChild($response->newElement('title', $cmisEntry['properties']['Name']['value'])); - $entry->appendChild($response->newField('updated', self::formatDatestamp())); + $entry->appendChild($feed->newField('published', self::formatDatestamp())); + $entry->appendChild($feed->newElement('summary', $cmisEntry['properties']['name']['value'])); + $entry->appendChild($feed->newElement('title', $cmisEntry['properties']['name']['value'])); + $entry->appendChild($feed->newField('updated', self::formatDatestamp())); // main CMIS entry - $objectElement = $response->newElement('cmis:object'); - $propertiesElement = $response->newElement('cmis:properties'); + $objectElement = $feed->newElement('cmisra:object'); + $objectElement->appendChild(self::createEntryPropertiesElement($feed, $cmisEntry['properties'])); + $entry->appendChild($objectElement); + + // TODO check determination of when to add app:edited tag +// if ($method == 'POST') { + $entry->appendChild($feed->newElement('app:edited', self::formatDatestamp())); +// } - foreach($cmisEntry['properties'] as $propertyName => $property) + // TODO pathSegment entry + + // deal with child objects + if (isset($cmisEntry['children'])) { + // add children node and fill with child entries + $childrenFeed = $feed->newElement('feed'); + self::createObjectChildrenFeed($childrenFeed, $cmisEntry['children'], $workspace, $feed, '' /*folderName not passed through*/); + + $childrenElement = $feed->newElement('cmisra:children'); + $childrenElement->appendChild($childrenFeed); + $entry->appendChild($childrenElement); + } + } + + /** + * Shared function for creating an object properties node + * + * @param object $feed AtomPub response feed + * @param array $properties CMIS object properties + * @return object $propertiesElement AtomPub node + */ + // TODO leave out unset properties? + static public function createEntryPropertiesElement(&$feed, $properties) + { + $propertiesElement = $feed->newElement('cmis:properties'); + foreach($properties as $propertyName => $property) { - $propElement = $response->newElement('cmis:' . $property['type']); - $propElement->appendChild($response->newAttr('cmis:name', $propertyName)); + $propElement = $feed->newElement('cmis:' . $property['type']); +// $propElement->appendChild($feed->newAttr('localName', 'rep-cmis:' . $propertyName)); + $propElement->appendChild($feed->newAttr('propertyDefinitionId', 'cmis:' . $propertyName)); if (!empty($property['value'])) { - if ($propertyName == 'ContentStreamUri') { + if ($propertyName == 'contentStreamUri') { $property['value'] = CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' .$property['value']; } - $response->newField('cmis:value', CMISUtil::boolToString($property['value']), $propElement); + $feed->newField('cmis:value', CMISUtil::boolToString($property['value']), $propElement); } $propertiesElement->appendChild($propElement); } - - $objectElement->appendChild($propertiesElement); - $entry->appendChild($objectElement); - // after every entry, append a cmis:terminator tag - $entry->appendChild($response->newElement('cmis:terminator')); - - // TODO check determination of when to add app:edited tag -// if ($method == 'POST') { - $entry->appendChild($response->newElement('app:edited', self::formatDatestamp())); -// } + return $propertiesElement; } /** @@ -367,21 +431,6 @@ class KT_cmis_atom_service_helper { $element = $feed->newField('name', 'admin', $feedElement); $feed->appendChild($feedElement); - // NOTE spec says this link MUST be present but is vague on where it points - // as of 0.61c: - // "The source link relation points to the underlying CMIS Type Definition as Atom Entry" - // so what is the underlying CMIS Type Definition for a collection of base types? - // suspect that it only applies when not listing all types, i.e. a base type is asked for - /* - $link = $feed->newElement('link'); - $link->appendChild($feed->newAttr('rel','source')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['typeId']))); - $feed->appendChild($link); - */ - - // current time: format = 2009-07-13T14:49:27.659+02:00 - $feed->appendChild($feed->newElement('updated', self::formatDatestamp())); - foreach($types as $type) { $entry = $feed->newEntry(); @@ -389,48 +438,37 @@ class KT_cmis_atom_service_helper { $feedElement = $feed->newField('author'); $element = $feed->newField('name', 'admin', $feedElement); $entry->appendChild($feedElement); - $feedElement = $feed->newField('content', $type['typeId']); + $feedElement = $feed->newField('content', 'Type definition for ' . $type['baseId']); $entry->appendChild($feedElement); - $feed->newField('id', 'urn:uuid:type-' . $type['typeId'], $feed); - - // TODO add parents link when not selecting a base type. - // TODO add children link when type has children - // TODO add descendants link when type has children - // NOTE KnowledgeTree currently only supports base types so these are not important at the present time. - + $feed->newField('id', 'urn:uuid:type-' . $type['baseId'], $feed); + // links $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','self')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['typeId']))); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); $entry->appendChild($link); // TODO type link MUST point to base type // KnowledgeTree currently only supports base types so this is not important // at the present time as it will always point at the base type. $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','type')); - $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['typeId']))); + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/type/' . strtolower($type['baseId']))); $entry->appendChild($link); $link = $feed->newElement('link'); $link->appendChild($feed->newAttr('rel','repository')); $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . '/servicedocument')); $entry->appendChild($link); - - $entry->appendChild($feed->newElement('summary', $type['typeId'] . ' Type')); - $entry->appendChild($feed->newElement('title', $type['typeId'])); + + $entry->appendChild($feed->newElement('summary', 'Summary for ' . $type['baseId'] . ' type')); + $entry->appendChild($feed->newElement('title', $type['baseId'])); $entry->appendChild($feed->newElement('updated', self::formatDatestamp())); - - // main CMIS entry - $feedElement = $feed->newElement('cmis:' . strtolower($type['typeId']) . 'Type'); - foreach($type as $property => $value) - { - $feed->newField('cmis:' . $property, CMISUtil::boolToString($value), $feedElement); - } - - $entry->appendChild($feedElement); - // after every entry, append a cmis:terminator tag - $entry->appendChild($feed->newElement('cmis:terminator')); + $objectElement = $feed->newElement('cmisra:type'); + foreach($type as $property => $value) { + $feed->newField('cmis:' . $property, CMISUtil::boolToString($value), $objectElement); + } + $entry->appendChild($objectElement); } return $feed; @@ -477,24 +515,113 @@ class KT_cmis_atom_service_helper { ++$start; } - return CMISUtil::encodeObjectId('Folder', $folderId); + return CMISUtil::encodeObjectId(FOLDER, $folderId); + } + + static public function getCmisObjectProperties(&$xml) + { + $xmlReader = new XMLReader(); + $xmlReader->XML($xml); + $object = false; + $objectProperties = false; + $cmisObjectProperty = null; + $cmisObjectPropertiesCollection = array(); + while ($xmlReader->read()) { + // get cmis object properties + if ($xmlReader->name == 'cmisra:object') { + $object = ($xmlReader->nodeType == XMLReader::ELEMENT); + // exit if we have finished reading the cmis object node + if (!$object) { + break; + } + } + else if ($object && ($xmlReader->name == 'cmis:properties')) { + $objectProperties = ($xmlReader->nodeType == XMLReader::ELEMENT); + } + else if ($objectProperties && ($xmlReader->nodeType == XMLReader::ELEMENT)) { + if (strstr($xmlReader->name, 'cmis:property') && $xmlReader->nodeType == XMLReader::ELEMENT) { + $cmisObjectProperty = $xmlReader->getAttribute('propertyDefinitionId'); + } + else if ($xmlReader->name == 'cmis:value' && $xmlReader->nodeType == XMLReader::ELEMENT) { + // push to next read, which will be the text contained within the node + $xmlReader->read(); + $cmisObjectPropertiesCollection[$cmisObjectProperty] = $xmlReader->value; + // reset for next value - may leave this out of final code + $cmisObjectProperty = null; + } + } + } + + return $cmisObjectPropertiesCollection; + } + + static public function getCmisContent(&$xml) + { + $xmlReader = new XMLReader(); + $xmlReader->XML($xml); + $content = false; + $cmisContentProperty = null; + $cmisObjectContent = array(); + while ($xmlReader->read()) { + if ($xmlReader->name == 'cmisra:content') { + $content = ($xmlReader->nodeType == XMLReader::ELEMENT); + // exit if we have finished reading the cmis content node + if (!$content) { + break; + } + } + else if ($content && ($xmlReader->nodeType == XMLReader::ELEMENT)) { + $cmisContentProperty = $xmlReader->name; + // push to next read, which will be the text contained within the node + $xmlReader->read(); + $cmisObjectContent[$cmisContentProperty] = $xmlReader->value; + } + } + + return $cmisObjectContent; + } + + static public function getAtomValues(&$xml, $tag) + { + $returnTag = null; + + $xmlReader = new XMLReader(); + $xmlReader->XML($xml); + $foundTag = false; + while ($xmlReader->read()) { + // using strstr because we may or may not have the tag preceded by "atom:" + // TODO ensure that this does not return incorrect matches + if (strstr($xmlReader->name, $tag)) { + $foundTag = ($xmlReader->nodeType == XMLReader::ELEMENT); + // exit if we have finished reading the cmis content node + if ($foundTag) { + $xmlReader->read(); + $returnTag = $xmlReader->value; + } + else { + break; + } + } + } + + return $returnTag; } - static public function getCmisProperties($xmlArray) + static public function getCmisPropertiesOld($xmlArray) { $properties = array(); - // find cmis:object tag - $baseCmisObject = KT_cmis_atom_service_helper::findTag('cmis:object', $xmlArray, null, false); + // find cmisra:object tag + $baseCmisObject = KT_cmis_atom_service_helper::findTag('cmisra:object', $xmlArray, null, false); if(count($baseCmisObject) <= 0) { $entryObject = KT_cmis_atom_service_helper::findTag('entry', $xmlArray, null, false); - $baseCmisObject = KT_cmis_atom_service_helper::findTag('cmis:object', $entryObject['@children'], null, true); + $baseCmisObject = KT_cmis_atom_service_helper::findTag('cmisra:object', $entryObject['@children'], null, true); } - if(count($baseCmisObject)>0) + if(count($baseCmisObject) > 0) { - foreach($baseCmisObject['@children'] as $key => $childElement) + foreach($baseCmisObject[0]['@children'] as $key => $childElement) { if ($key == 'cmis:properties') { @@ -502,7 +629,8 @@ class KT_cmis_atom_service_helper { { foreach($cmisPropertyDefinition as $propertyType => $propertyDefinition) { - $properties[$propertyDefinition['@attributes']['cmis:name']] = $propertyDefinition['@children']['cmis:value'][0]['@value']; + $properties[$propertyDefinition['@attributes']['cmis:name']] + = $propertyDefinition['@children']['cmis:value'][0]['@value']; } } } @@ -512,7 +640,7 @@ class KT_cmis_atom_service_helper { return $properties; } - static public function getAtomValues($xmlArray, $tag) + static public function getAtomValuesOld($xmlArray, $tag) { if (!is_null($xmlArray['atom:'.$tag])) return $xmlArray['atom:'.$tag][0]['@value']; @@ -562,12 +690,15 @@ class KT_cmis_atom_service_helper { static public function getContentStream(&$service, &$ObjectService, $repositoryId) { $response = $ObjectService->getProperties($repositoryId, $service->params[0], false, false); - if (PEAR::isError($response)) { + if ($response['status_code'] == 1) { return null; } $contentStream = $ObjectService->getContentStream($repositoryId, $service->params[0]); + // hack for removing one level of access + $contentStream = $contentStream['results']; + return $contentStream; } /** @@ -580,17 +711,20 @@ class KT_cmis_atom_service_helper { static public function downloadContentStream(&$service, &$ObjectService, $repositoryId) { $response = $ObjectService->getProperties($repositoryId, $service->params[0], false, false); - if (PEAR::isError($response)) { - $feed = KT_cmis_atom_service_helper::getErrorFeed($service, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response->getMessage()); + if ($response['status_code'] == 1) { + $feed = KT_cmis_atom_service_helper::getErrorFeed($service, KT_cmis_atom_service::STATUS_SERVER_ERROR, $response['message']); $service->responseFeed = $feed; return null; } + else { + $response = $response['results']; + } // TODO also check If-Modified-Since? // $service->headers['If-Modified-Since'] => 2009-07-24 17:16:54 $service->setContentDownload(true); - $eTag = md5($response['properties']['LastModificationDate']['value'] . $response['properties']['ContentStreamLength']['value']); + $eTag = md5($response['properties']['lastModificationDate']['value'] . $response['properties']['contentStreamLength']['value']); if ($service->headers['If-None-Match'] == $eTag) { @@ -601,24 +735,28 @@ class KT_cmis_atom_service_helper { $contentStream = $ObjectService->getContentStream($repositoryId, $service->params[0]); + // hack for removing one level of access + $contentStream = $contentStream['results']; + // headers specific to output $service->setEtag($eTag); - $service->setHeader('Last-Modified', $response['properties']['LastModificationDate']['value']); + $service->setHeader('Last-Modified', $response['properties']['lastModificationDate']['value']); - if (!empty($response['properties']['ContentStreamMimeType']['value'])) { - $service->setHeader('Content-type', $response['properties']['ContentStreamMimeType']['value'] . ';charset=utf-8'); + if (!empty($response['properties']['contentStreamMimeType']['value'])) { + $service->setHeader('Content-type', $response['properties']['contentStreamMimeType']['value'] . ';charset=utf-8'); } else { $service->setHeader('Content-type', 'text/plain;charset=utf-8'); } - $service->setHeader('Content-Disposition', 'attachment;filename="' . $response['properties']['ContentStreamFilename']['value'] . '"'); - $service->setHeader('Content-Length', $response['properties']['ContentStreamLength']['value']); + $service->setHeader('Content-Disposition', 'attachment;filename="' . $response['properties']['contentStreamFilename']['value'] . '"'); + $service->setHeader('Content-Length', $response['properties']['contentStreamLength']['value']); $service->setOutput($contentStream); } //TODO: Add key information to be able to find the same tag in the original struct (MarkH) - static public function findTag($tagName=NULL,$xml=array(),$tagArray=NULL,$deep=false){ + static public function findTag($tagName=NULL,$xml=array(),$tagArray=NULL,$deep=false) + { $tagArray=is_array($tagArray)?$tagArray:array(); foreach($xml as $xmlTag=>$content){ if($xmlTag===$tagName){ diff --git a/webservice/atompub/cmis/index.php b/webservice/atompub/cmis/index.php index 5774ec3..f4601fe 100644 --- a/webservice/atompub/cmis/index.php +++ b/webservice/atompub/cmis/index.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -49,6 +49,7 @@ define('KT_ATOM_LIB_FOLDER', '../../classes/atompub/'); define('CMIS_APP_BASE_URI', trim(KT_APP_BASE_URI, '/')); define('CMIS_APP_SYSTEM_URI', KT_APP_SYSTEM_URI); define('CMIS_ATOM_LIB_FOLDER', trim(KT_ATOM_LIB_FOLDER, '/') . '/cmis/'); +define('CMIS_API', KT_LIB_DIR . '/api/ktcmis'); /** * Check Realm Authentication @@ -101,12 +102,12 @@ if ($workspace == 'servicedocument') */ // TODO consider a registerServices function which will, dependant on what is requested, register the appropriate services, keep the logic out of the index file $APP->registerService('dms', 'folder', 'KT_cmis_atom_service_folder', 'Root Folder Children Collection', - array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'children'), 'rootchildren'); + array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'children'), 'root'); $APP->registerService('dms', 'folder', 'KT_cmis_atom_service_folder', 'Root Folder Children Collection', array(rawurlencode($APP->repositoryInfo['rootFolderId']), 'descendants'), 'rootdescendants'); $APP->registerService('dms', 'checkedout', 'KT_cmis_atom_service_checkedout', 'Checked Out Document Collection', null, 'checkedout', 'application/atom+xml;type=entry'); -$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'typeschildren'); +$APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'types'); $APP->registerService('dms', 'types', 'KT_cmis_atom_service_types', 'Object Type Collection', null, 'typesdescendants'); if ($workspace != 'servicedocument') diff --git a/webservice/atompub/demodms/KT_atom_service_helper.inc.php b/webservice/atompub/demodms/KT_atom_service_helper.inc.php index aa52e93..ff6dc0d 100644 --- a/webservice/atompub/demodms/KT_atom_service_helper.inc.php +++ b/webservice/atompub/demodms/KT_atom_service_helper.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/webservice/atompub/index.php b/webservice/atompub/index.php index 86924a8..9385619 100644 --- a/webservice/atompub/index.php +++ b/webservice/atompub/index.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/webservice/classes/atompub/KT_atom_baseDoc.inc.php b/webservice/classes/atompub/KT_atom_baseDoc.inc.php index ef9007b..53ffdc1 100644 --- a/webservice/classes/atompub/KT_atom_baseDoc.inc.php +++ b/webservice/classes/atompub/KT_atom_baseDoc.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/webservice/classes/atompub/KT_atom_service.inc.php b/webservice/classes/atompub/KT_atom_service.inc.php index c20f178..4f095a3 100644 --- a/webservice/classes/atompub/KT_atom_service.inc.php +++ b/webservice/classes/atompub/KT_atom_service.inc.php @@ -68,12 +68,14 @@ class KT_atom_service{ public $parsedXMLContent=''; public $headers=array(); - public function __construct($method,$params,$content){ + public function __construct($method,$params,$content,$parse = true){ $this->method=$method; $this->params=$params; $this->rawContent=$content; $this->parseHeaders(); - $this->parsedXMLContent=$this->xml2array($this->rawContent); + if ($parse) { + $this->parsedXMLContent=$this->xml2array($this->rawContent); + } $this->setStatus(self::STATUS_OK); $this->responseFeed=new KT_atom_responseFeed(KT_APP_BASE_URI); switch(strtoupper($this->method)){ diff --git a/webservice/classes/atompub/KT_atom_serviceDoc.inc.php b/webservice/classes/atompub/KT_atom_serviceDoc.inc.php index 236d6eb..1d2fe14 100644 --- a/webservice/classes/atompub/KT_atom_serviceDoc.inc.php +++ b/webservice/classes/atompub/KT_atom_serviceDoc.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -43,7 +43,6 @@ */ include_once('KT_atom_baseDoc.inc.php'); - class KT_atom_serviceDoc extends KT_atom_baseDoc { protected $baseURI=NULL; @@ -97,11 +96,6 @@ class KT_atom_serviceDoc extends KT_atom_baseDoc { - - - - - /** diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php index aaeb22c..2f3319a 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_response.inc.php @@ -1,6 +1,6 @@ feed->appendChild($this->newAttr('xmlns:app', 'http://www.w3.org/2007/app')); - $this->feed->appendChild($this->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200901')); + $this->feed->appendChild($this->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200908/')); + $this->feed->appendChild($this->newAttr('xmlns:cmisra', 'http://docs.oasis-open.org/ns/cmis/restatom/200908/')); // require the workspace for creating links within responses $queryArray = split('/', trim($_SERVER['QUERY_STRING'], '/')); @@ -37,55 +38,6 @@ class KT_cmis_atom_responseFeed extends KT_atom_responseFeed { $this->feed->appendChild($element); } - // this is ALL going away...adjust all calling code... - /* - protected function constructHeader() - { - if (!is_null($this->id)) - { - $this->newId($this->id, $this->feed); - } - - $link = $this->newElement('link'); - $link->appendChild($this->newAttr('rel','self')); - $link->appendChild($this->newAttr('href', $this->baseURI . trim($_SERVER['QUERY_STRING'], '/'))); - $feed->appendChild($link); - - if (!is_null($this->title)) - { - $this->feed->appendChild($this->newElement('title', $this->title)); - } - - $this->DOM->appendChild($this->feed); - } - - public function &newId($id, $entry = null) - { - $id = $this->newElement('id', $id); - if(isset($entry))$entry->appendChild($id); - return $id; - } - - public function &newField($name = NULL, $value = NULL, &$entry = NULL) - { - $append = false; - - if(func_num_args() > 3) - { - $append = ((func_get_arg(3) === true) ? true : false); - } - - $field = $this->newElement($name, $value); - - if (isset($entry)) $entry->appendChild($field); - else if ($append) $this->feed->appendChild($field); - - return $field; - } -\ - * - */ - } class KT_cmis_atom_ResponseFeed_GET extends KT_cmis_atom_responseFeed{} @@ -93,4 +45,4 @@ class KT_cmis_atom_ResponseFeed_PUT extends KT_cmis_atom_responseFeed{} class KT_cmis_atom_ResponseFeed_POST extends KT_cmis_atom_responseFeed{} class KT_cmis_atom_ResponseFeed_DELETE extends KT_cmis_atom_responseFeed{} -?> \ No newline at end of file +?> diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php index de38ebe..417121d 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_server.inc.php @@ -1,7 +1,7 @@ getRepositories(); + + // hack for removing one level of access + $repositories = $repositories['results']; + // fetch for default first repo; NOTE that this will probably have to change at some point, quick and dirty for now - $this->repositoryInfo = $RepositoryService->getRepositoryInfo($repositories[0]['repositoryId']); + // hack for removing one level of access + $repositoryInfo = $RepositoryService->getRepositoryInfo($repositories[0]['repositoryId']); + $this->repositoryInfo = $repositoryInfo['results']; } } @@ -84,7 +89,7 @@ class KT_cmis_atom_server extends KT_atom_server { $ws->appendChild($service->newAttr('cmis:repositoryRelationship', $this->repositoryInfo['repositoryRelationship'])); // repository information - $element = $service->newElement('cmis:repositoryInfo'); + $element = $service->newElement('cmisra:repositoryInfo'); foreach($this->repositoryInfo as $key => $repoData) { if ($key == 'rootFolderId') { @@ -116,6 +121,11 @@ class KT_cmis_atom_server extends KT_atom_server { } } } + +// ob_start(); +// readfile('C:\Users\Paul\Documents\Downloads\cmis_mod_kt.xml'); +// $this->output = ob_get_contents(); +// ob_end_clean(); $this->output = $service->getAPPdoc(); } @@ -150,11 +160,17 @@ class KT_cmis_atom_server extends KT_atom_server { public function render() { - ob_end_clean(); - if (!$this->headersSet) header('Content-type: text/xml'); - if ($this->renderBody) echo $this->output; + ob_end_clean(); + + if (!$this->headersSet) { + header('Content-type: text/xml'); + } + + if ($this->renderBody) { + echo $this->output; + } } } -?> \ No newline at end of file +?> diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_service.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_service.inc.php index acdf7d3..1ae6fc8 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_service.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_service.inc.php @@ -9,12 +9,21 @@ class KT_cmis_atom_service extends KT_atom_service { protected $serviceType = null; protected $contentDownload = false; + public function __construct($method, $params, $content) + { + // We are not going to use the parsed xml content, but rather the raw content; + // This is due to changes in the CMIS spec more than once requiring a change in + // the functions which fetch information from the parsed content; using XMLReader + // now which should be able to handle changes easier + parent::__construct($method, $params, $content, false); + } + public function setContentDownload($contentDownload) { $this->contentDownload = $contentDownload; } - public public function isContentDownload() + public function isContentDownload() { return $this->contentDownload; } diff --git a/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php b/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php index 6a22962..3cf8c2b 100644 --- a/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php +++ b/webservice/classes/atompub/cmis/KT_cmis_atom_serviceDoc.inc.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -43,11 +43,11 @@ * Includes */ include_once(KT_ATOM_LIB_FOLDER.'KT_atom_serviceDoc.inc.php'); -//include_once('KT_atom_baseDoc.inc.php'); +require_once(CMIS_API . '/ktRepositoryService.inc.php'); class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { -// override and extend as needed + // override and extend as needed public $repositoryInfo = array(); @@ -58,16 +58,18 @@ class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { // get repositoryInfo // NOTE currently we only support one repository, which will be the first one found in the repositories.xml config // TODO multiple repositories as individual workspaces - - include 'services/cmis/RepositoryService.inc.php'; - $RepositoryService = new RepositoryService(); - // TODO add auth requirement here, don't want to even supply service doc without auth -// $RepositoryService->startSession(); + $RepositoryService = new KTRepositoryService(); // fetch data for response $repositories = $RepositoryService->getRepositories(); + + // hack for removing one level of access + $repositories = $repositories['results']; + // fetch for default first repo; NOTE that this will probably have to change at some point, quick and dirty for now - $this->repositoryInfo = $RepositoryService->getRepositoryInfo($repositories[0]['repositoryId']); + // hack for removing one level of access + $repositoryInfo = $RepositoryService->getRepositoryInfo($repositories[0]['repositoryId']); + $this->repositoryInfo = $repositoryInfo['results']; } protected function constructServiceDocumentHeaders() @@ -75,7 +77,8 @@ class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { $service = $this->newElement('service'); $service->appendChild($this->newAttr('xmlns', 'http://www.w3.org/2007/app')); $service->appendChild($this->newAttr('xmlns:atom', 'http://www.w3.org/2005/Atom')); - $service->appendChild($this->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200901')); + $service->appendChild($this->newAttr('xmlns:cmis', 'http://docs.oasis-open.org/ns/cmis/core/200908/')); + $service->appendChild($this->newAttr('xmlns:cmisra', 'http://docs.oasis-open.org/ns/cmis/restatom/200908/')); $this->service =& $service; $this->DOM->appendChild($this->service); } @@ -84,8 +87,8 @@ class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { { $collection=$this->newElement('collection'); $collection->appendChild($this->newAttr('href', $url)); - $collection->appendChild($this->newAttr('cmis:collectionType', $cmisCollectionType)); $collection->appendChild($this->newElement('atom:title', $title)); + $collection->appendChild($this->newElement('cmisra:collectionType', $cmisCollectionType)); if (!is_null($accept)) { $collection->appendChild($this->newElement('accept', $accept)); } @@ -127,4 +130,4 @@ class KT_cmis_atom_serviceDoc extends KT_atom_serviceDoc { */ -?> \ No newline at end of file +?> diff --git a/webservice/classes/atompub/cmis/NavigationService.inc.php b/webservice/classes/atompub/cmis/NavigationService.inc.php deleted file mode 100644 index 0be43c9..0000000 --- a/webservice/classes/atompub/cmis/NavigationService.inc.php +++ /dev/null @@ -1,127 +0,0 @@ - diff --git a/webservice/classes/atompub/cmis/ObjectService.inc.php b/webservice/classes/atompub/cmis/ObjectService.inc.php deleted file mode 100644 index f1d007d..0000000 --- a/webservice/classes/atompub/cmis/ObjectService.inc.php +++ /dev/null @@ -1,175 +0,0 @@ - diff --git a/webservice/classes/atompub/cmis/RepositoryService.inc.php b/webservice/classes/atompub/cmis/RepositoryService.inc.php deleted file mode 100644 index c763eb3..0000000 --- a/webservice/classes/atompub/cmis/RepositoryService.inc.php +++ /dev/null @@ -1,85 +0,0 @@ - diff --git a/webservice/classes/atompub/cmis/VersioningService.inc.php b/webservice/classes/atompub/cmis/VersioningService.inc.php deleted file mode 100644 index 07a953c..0000000 --- a/webservice/classes/atompub/cmis/VersioningService.inc.php +++ /dev/null @@ -1,102 +0,0 @@ - diff --git a/webservice/classes/rest/Server.php b/webservice/classes/rest/Server.php index 805298a..3891b41 100644 --- a/webservice/classes/rest/Server.php +++ b/webservice/classes/rest/Server.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -34,8 +34,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice diff --git a/webservice/classes/rest/model/RestService.class.php b/webservice/classes/rest/model/RestService.class.php index c03c310..4842c5d 100755 --- a/webservice/classes/rest/model/RestService.class.php +++ b/webservice/classes/rest/model/RestService.class.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice diff --git a/webservice/classes/soap/common.php b/webservice/classes/soap/common.php index c485c0e..456ff53 100755 --- a/webservice/classes/soap/common.php +++ b/webservice/classes/soap/common.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice diff --git a/webservice/classes/soap/model/RestService.class.php b/webservice/classes/soap/model/RestService.class.php index 7735706..185083b 100755 --- a/webservice/classes/soap/model/RestService.class.php +++ b/webservice/classes/soap/model/RestService.class.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice diff --git a/webservice/clienttools/services/0.9/kt.php b/webservice/clienttools/services/0.9/kt.php index a5d01d1..7b1bba5 100644 --- a/webservice/clienttools/services/0.9/kt.php +++ b/webservice/clienttools/services/0.9/kt.php @@ -94,6 +94,31 @@ class kt extends client_service { $this->setResponse ( $result ); return true; } + + function get_checkedout_documents_list($params) { + $this->logTrace ((__METHOD__.'('.__FILE__.' '.__LINE__.')'), 'Enter Function' ); + $kt = &$this->KT; + + + $params ['control'] = 'F_'; + $params ['node'] = substr ( $params ['node'], strlen ( $params ['control'] ) ); + + $folder = &$kt->get_folder_by_id ( $params ['node'] ); + if (! $this->checkPearError ( $folder, "[error 1] Folder Not Found: {$params['control']}{$params['node']}", '', array () )) + return false; + + $types = (isset ( $params ['types'] ) ? $params ['types'] : 'D'); + $listing = $folder->get_listing ( 1, $types ); + foreach ( $listing as $item ) { + if ($item['checked_out_by'] == $params['user']) + { + $result[] = array ('text' => htmlspecialchars ( $item ['title'] ), 'id' => $item ['id'], 'filename' => $item ['filename']); + } + } + + $this->setResponse ( $result ); + return true; + } function get_folder_contents($params) { $this->logTrace ((__METHOD__.'('.__FILE__.' '.__LINE__.')'), 'Enter Function' ); @@ -263,7 +288,7 @@ class kt extends client_service { if ($item ['filesize'] == 'n/a') { $item ['filesize'] = - 1; } - return array ('text' => htmlspecialchars ( $item ['title'] ), 'originaltext' => $item ['title'], 'id' => $item ['document_id'], 'filename' => $item ['filename'], 'cls' => $class, 'leaf' => true, 'document_type' => $item ['document_type'], 'item_type' => 'D', 'permissions' => $item ['permissions'], 'content_id' => $item ['content_id'], 'filesize' => $item ['filesize'], 'modified' => $item ['modified_date'], 'created_date' => $item ['created_date'], 'checked_out_by' => $item ['checked_out_by'], 'relevance' => $item ['relevance'], 'qtip' => $qtip, 'version' => $item ['version'], 'is_immutable' => $item ['is_immutable'] ); + return array ('text' => htmlspecialchars ( $item ['title'] ), 'originaltext' => $item ['title'], 'id' => $item ['document_id'], 'filename' => $item ['filename'], 'cls' => $class, 'leaf' => true, 'document_type' => $item ['document_type'], 'item_type' => 'D', 'permissions' => $item ['permissions'], 'content_id' => $item ['content_id'], 'filesize' => $item ['filesize'], 'modified' => $item ['modified_date'], 'created_date' => $item ['created_date'], 'checked_out_by' => $item ['checked_out_by'], 'relevance' => $item ['relevance'], 'qtip' => $qtip, 'version' => $item ['version'], 'is_immutable' => $item ['is_immutable'], 'folder_id' => $item['folder_id'] ); } private function _processItemInclusion_grid($item, $class, $qtip) { @@ -1346,6 +1371,118 @@ Fatal error: Cannot unset string offsets in on line 981 return true; } + /** + * Method to get the recently viewed documents and folders. + */ + function get_recently_viewed() + { + $this->logTrace ((__METHOD__.'('.__FILE__.' '.__LINE__.')'), 'Enter Function' ); + $kt = &$this->KT; + + + // Generate Folders List + $returnFoldersArray = array(); + + $folders = $kt->getRecentlyViewedFolders(); + foreach ($folders as $folder) + { + $folderObj = &$kt->get_folder_by_id ( $folder->getFolderId() ); + + $folderArray = array(); + $folderArray['id'] = $folderObj->folderid; + $folderArray['name'] = $folderObj->get_folder_name(); + + $parentIds = explode(',', $folderObj->getParentFolderIds()); + $path = '/F_0'; + + if (count($parentIds) > 0 && $folderObj->getParentFolderIds() != '') { + foreach ($parentIds as $parentId) + { + $path .= '/F_'.$parentId; + } + } + + $path .= '/F_'.$folderObj->folderid; + + $folderArray['path'] = $path; + + $returnFoldersArray[] = $folderArray; + } + + + // Generate Documents List + $returnDocumentArray = array(); + + $items = $kt->getRecentlyViewedDocuments(); + foreach ($items as $item) + { + $document = $kt->get_document_by_id($item->getDocumentId()); + $documentDetail = $document->get_detail(); + + $documentArray = array(); + + $documentArray['id'] = $document->documentid; + $documentArray['contentID'] = $document->documentid; + $documentArray['title'] = $documentDetail['title']; + $documentArray['folderId'] = $documentDetail['folder_id']; + + // Determine Icon Class + $extpos = strrpos ( $documentDetail['filename'], '.' ); + if ($extpos === false) { + $class = 'file-unknown'; + } else { + $class = 'file-' . substr ( $documentDetail['filename'], $extpos + 1 ); // Get Extension without the dot + } + $documentArray['iconCls'] = $class; + + // Determine Icon Path + $folderObj = $kt->get_folder_by_id ( $documentDetail['folder_id']); + $parentIds = explode(',', $folderObj->getParentFolderIds()); + $path = '/F_0'; + if (count($parentIds) > 0 && $folderObj->getParentFolderIds() != '') { + foreach ($parentIds as $parentId) + { + $path .= '/F_'.$parentId; + } + } + $path .= '/F_'.$documentDetail['folder_id']; + + $documentArray['folderPath'] = $path; + + $returnDocumentArray[] = $documentArray; + } + + $this->setResponse(array('documents'=>$returnDocumentArray, 'folders'=>$returnFoldersArray)); + } + + + function get_folder_path($arr) + { + $kt=&$this->KT; + + $folderObj = &$kt->get_folder_by_id ( $arr ['folderId'] ); + if (PEAR::isError ( $folderObj )) { + $this->setError ( "Could not get folder by Id: {$arr['folderId']}" ); + $this->setDebug ( 'FolderError', array ('kt' => $kt, 'folder' => $folderObj ) ); + return false; + } + + $parentIds = explode(',', $folderObj->getParentFolderIds()); + $path = '/F_0'; + + if (count($parentIds) > 0 && $folderObj->getParentFolderIds() != '') { + foreach ($parentIds as $parentId) + { + $path .= '/F_'.$parentId; + } + } + + $path .= '/F_'.$folderObj->folderid; + + + $this->setResponse ( array ('status_code' => 0, 'folderPath' => $path ) ); + } + diff --git a/webservice/clienttools/services/mdownload.php b/webservice/clienttools/services/mdownload.php index eb90c1e..b9665a2 100644 --- a/webservice/clienttools/services/mdownload.php +++ b/webservice/clienttools/services/mdownload.php @@ -6,7 +6,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple - * Copyright (C) 2008, 2009 KnowledgeTree Inc. + * Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under diff --git a/webservice/documentation.php b/webservice/documentation.php index 8429ca0..145dab9 100755 --- a/webservice/documentation.php +++ b/webservice/documentation.php @@ -5,7 +5,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -33,8 +33,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice diff --git a/webservice/restservice.php b/webservice/restservice.php index fa118da..2ba9aba 100755 --- a/webservice/restservice.php +++ b/webservice/restservice.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice diff --git a/webservice/webservice.php b/webservice/webservice.php index ba9edd5..301a62c 100755 --- a/webservice/webservice.php +++ b/webservice/webservice.php @@ -4,7 +4,7 @@ * * KnowledgeTree Community Edition * Document Management Made Simple -* Copyright (C) 2008,2009 KnowledgeTree Inc. +* Copyright (C) 2008, 2009, 2010 KnowledgeTree Inc. * * * This program is free software; you can redistribute it and/or modify it under @@ -32,8 +32,12 @@ * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. +* Contributor( s): ______________________________________ +*/ + +/** * -* @copyright 2008-2009, KnowledgeTree Inc. +* @copyright 2008-2010, KnowledgeTree Inc. * @license GNU General Public License version 3 * @author KnowledgeTree Team * @package Webservice