diff --git a/HISTORY.txt b/HISTORY.txt
index ac170c3..21848fd 100644
--- a/HISTORY.txt
+++ b/HISTORY.txt
@@ -58,7 +58,7 @@ Improvements
New Features
* Search queries saveable on per user-basis
- * The login page can now be customised through-the-web.
+ * The login page can now be customized through-the-web.
* Admin can now disable the "preferences" dialog for all users.
* Ensuring that your system is "transaction safe" can now be done
through the web interface.
diff --git a/config/config.ini b/config/config.ini
index d186d2f..7d83893 100644
--- a/config/config.ini
+++ b/config/config.ini
@@ -195,6 +195,10 @@ tmpDirectory = ${varDirectory}/tmp
graphicsUrl = ${rootUrl}/graphics
uiUrl = ${rootUrl}/presentation/lookAndFeel/knowledgeTree
+;dedicated feed url
+;dedicatedrsstitle = RSS Feed Title
+;dedicatedrssurl =
+
; files
stopwordsFile = ${fileSystemRoot}/config/stopwords.txt
diff --git a/i18n/knowledgeTree.pot b/i18n/knowledgeTree.pot
index 9847111..3922e11 100644
--- a/i18n/knowledgeTree.pot
+++ b/i18n/knowledgeTree.pot
@@ -3474,7 +3474,7 @@ msgid "Existing Searches"
msgstr ""
#: i18n/templates.c:5507
-msgid "Existing customised help pages"
+msgid "Existing customized help pages"
msgstr ""
#: i18n/templates.c:785
@@ -5622,7 +5622,7 @@ msgid "No groups or roles are defined in the DMS."
msgstr ""
#: i18n/templates.c:5519
-msgid "No help files have been customised."
+msgid "No help files have been customized."
msgstr ""
#: help.php:83 help.php:84
@@ -6405,7 +6405,7 @@ msgid "Please provide a search term"
msgstr ""
#: i18n/templates.c:782
-msgid "Please select a disclaimer to customise."
+msgid "Please select a disclaimer to customize."
msgstr ""
#: lib/authentication/builtinauthenticationprovider.inc.php:64
@@ -8427,7 +8427,7 @@ msgid "To connect with the #appname# Tools for Windows package, use this address
msgstr ""
#: i18n/templates.c:5504
-msgid "To customise a help file, please visit that file via the help system and click on customise this help file ."
+msgid "To customize a help file, please visit that file via the help system and click on customize this help file ."
msgstr ""
#: i18n/templates.c:1715
diff --git a/i18n/templates.c b/i18n/templates.c
index 42005b3..50c839f 100644
--- a/i18n/templates.c
+++ b/i18n/templates.c
@@ -779,7 +779,7 @@ gettext("Update");
gettext("Disclaimers");
/* ./templates/ktstandard/disclaimers/manage_disclaimers.smarty */
-gettext("Please select a disclaimer to customise.");
+gettext("Please select a disclaimer to customize.");
/* ./templates/ktstandard/disclaimers/manage_disclaimers.smarty */
gettext("Existing disclaimers");
@@ -5510,10 +5510,10 @@ gettext("Create Fieldset");
gettext("Current help assignments");
/* ./templates/ktcore/manage_help.smarty */
-gettext("To customise a help file, please visit that file via the help system and click on customise this help file .");
+gettext("To customize a help file, please visit that file via the help system and click on customize this help file .");
/* ./templates/ktcore/manage_help.smarty */
-gettext("Existing customised help pages");
+gettext("Existing customized help pages");
/* ./templates/ktcore/manage_help.smarty */
gettext("Name");
@@ -5525,7 +5525,7 @@ gettext("Actions");
gettext("Delete");
/* ./templates/ktcore/manage_help.smarty */
-gettext("No help files have been customised.");
+gettext("No help files have been customized.");
/* ./templates/ktcore/boolean_search_change.smarty */
gettext("Boolean Search");
diff --git a/ktwebdav/ktwebdav.php b/ktwebdav/ktwebdav.php
index dbc8484..cbdbbec 100644
--- a/ktwebdav/ktwebdav.php
+++ b/ktwebdav/ktwebdav.php
@@ -6,39 +6,40 @@
* KnowledgeTree Open Source Edition
* Document Management Made Simple
* Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
- *
+ *
* 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 The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
* Blake Street, Observatory, 7925 South Africa. 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
- * copyright notice.
+ * must display the words "Powered by KnowledgeTree" and retain the original
+ * copyright notice.
* Contributor( s): ______________________________________
*
*/
+ $webdav_pear_path = 'thirdparty/pear';
$kt_pear_path = '../thirdparty/pear';
$include_path = ini_get('include_path');
- ini_set('include_path', $kt_pear_path . PATH_SEPARATOR . $include_path);
+ ini_set('include_path', $webdav_pear_path . PATH_SEPARATOR . $kt_pear_path . PATH_SEPARATOR . $include_path);
require_once "lib/KTWebDAVServer.inc.php";
$ktwebdav = new KTWebDAVServer();
diff --git a/lib/documentmanagement/documentutil.inc.php b/lib/documentmanagement/documentutil.inc.php
index 55b5925..7e1dbf7 100644
--- a/lib/documentmanagement/documentutil.inc.php
+++ b/lib/documentmanagement/documentutil.inc.php
@@ -255,9 +255,6 @@ class KTDocumentUtil {
'creatorid' => $oUser->getID(),
'documenttypeid' => $iDocumentTypeId,
));
- if (PEAR::isError($oDocument)) {
- return $oDocument;
- }
if (is_null($oContents)) {
$res = KTDocumentUtil::setIncomplete($oDocument, 'contents');
@@ -551,34 +548,15 @@ class KTDocumentUtil {
// {{{ _in_add
function &_in_add($oFolder, $sFilename, $oUser, $aOptions) {
$aOrigOptions = $aOptions;
- if (KTDocumentUtil::fileExists($oFolder, $sFilename)) {
- $oDoc = Document::getByFilenameAndFolder($sFilename, $oFolder->getId());
- if (PEAR::isError($oDoc)) {
- return PEAR::raiseError(_kt('Document with that filename already exists in this folder, and appears to be invalid. Please contact the system administrator.'));
- } else {
- if ($oDoc->getStatusID() != LIVE) {
- $sError = _kt('Document with that filename already exists in this folder, but it has been archived or deleted and is still available for restoration. To prevent it being overwritten, you are not allowed to add a document with the same title or filename.');
- } else {
- $sError = _kt('Document with that filename already exists in this folder.');
- }
-
- $sError .= _kt(' Document') . ': ' . $oDoc->getName() . ' (ID:' . $oDoc->getId() . ')';
- return PEAR::raiseError($sError);
- }
+ while(KTDocumentUtil::fileExists($oFolder, $sFilename)) {
+ $oDoc = Document::getByFilenameAndFolder($sFilename, $oFolder->getId());
+ $sFilename = KTDocumentUtil::generateNewDocumentFilename($oDoc->getFileName());
}
$sName = KTUtil::arrayGet($aOptions, 'description', $sFilename);
- if (KTDocumentUtil::nameExists($oFolder, $sName)) {
- $oDoc = Document::getByNameAndFolder($sName, $oFolder->getId());
- if (PEAR::isError($oDoc)) {
- return PEAR::raiseError(_kt('Document with that title already exists in this folder, and appears to be invalid. Please contact the system administrator.'));
- } else {
- if ($oDoc->getStatusID != LIVE) {
- return PEAR::raiseError(_kt('Document with that title already exists in this folder, but it has been archived or deleted and is still available for restoration. To prevent it being overwritten, you are not allowed to add a document with the same title or filename.'));
- } else {
- return PEAR::raiseError(_kt('Document with that title already exists in this folder.'));
- }
- }
-
+ while(KTDocumentUtil::nameExists($oFolder, $sName)) {
+ $oDoc = Document::getByNameAndFolder($sName, $oFolder->getId());
+ $aOptions['description'] = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
+ $sName = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
}
$oUploadChannel =& KTUploadChannel::getSingleton();
@@ -660,7 +638,46 @@ class KTDocumentUtil {
return $oDocument;
}
// }}}
-
+
+ function generateNewDocumentFilename($sDocFilename){
+ if(preg_match("/\([0-9]+\)(\.[^\.]+){1,}$/", $sDocFilename)){
+ preg_match("/\([0-9]+\)\./", $sDocFilename, $matches);
+ $new_one = substr($matches[0], 1);
+ $new_two = explode(')', $new_one);
+ $new = $new_two[0]+1;
+
+ $pattern[0] = '/\([0-9]+\)\./';
+ $replacement[0] = ' ('.$new.').';
+ $sFilename = preg_replace($pattern, $replacement, $sDocFilename);
+ }else{
+ $matches = explode('.', $sDocFilename);
+ $prefix = $matches[0].' (2)';
+ for($i = 1; $i < count($matches); $i++ ){
+ $suffix .= '.'.$matches[$i];
+ }
+ $sFilename = $prefix.$suffix;
+ }
+
+ return $sFilename;
+ }
+
+ function generateNewDocumentName($sDocName){
+ if(preg_match("/\([0-9]+\)$/", $sDocName)){
+ preg_match("/\([0-9]+\)$/", $sDocName, $matches);
+ $new_one = substr($matches[0], 1);
+ $new_two = explode(')', $new_one);
+ $new = $new_two[0]+1;
+
+ $pattern[0] = '/\([0-9]+\)$/';
+ $replacement[0] = '('.$new.')';
+ $sName = preg_replace($pattern, $replacement, $sDocName);
+ }else{
+ $sName = $sDocName.' (2)';
+ }
+
+ return $sName;
+ }
+
// {{{ fileExists
function fileExists($oFolder, $sFilename) {
return Document::fileExists($sFilename, $oFolder->getID());
@@ -1228,7 +1245,7 @@ class KTDocumentUtil {
class KTMetadataValidationError extends PEAR_Error {
function KTMetadataValidationError ($aFailed) {
$this->aFailed = $aFailed;
- $message = _kt('Validation Failed');
+ $message = _kt('Please be sure to enter information for all the Required fields below');
parent::PEAR_Error($message);
}
}
diff --git a/plugins/ktcore/KTBulkActions.php b/plugins/ktcore/KTBulkActions.php
index dfc21dc..e2d53fa 100644
--- a/plugins/ktcore/KTBulkActions.php
+++ b/plugins/ktcore/KTBulkActions.php
@@ -647,6 +647,9 @@ class KTBrowseBulkExportAction extends KTBulkAction {
$sFolderId = $oFolder->getId();
$sFolderDocs = $oFolder->getDocumentIDs($sFolderId);
+ // Add folder to zip
+ $this->oZip->addFolderToZip($oFolder);
+
if(!empty($sFolderDocs)){
$aDocuments = explode(',', $sFolderDocs);
}
@@ -725,7 +728,13 @@ class KTBrowseBulkCheckoutAction extends KTBulkAction {
function check_entity($oEntity) {
if(is_a($oEntity, 'Document')) {
if ($oEntity->getIsCheckedOut()) {
- return PEAR::raiseError(_kt('Document is already checked out'));
+ $checkedOutUser = $oEntity->getCheckedOutUserID();
+ $sUserId = $_SESSION['userID'];
+
+ if($checkedOutUser != $sUserId){
+ $oCheckedOutUser = User::get($checkedOutUser);
+ return PEAR::raiseError($oEntity->getName().': '._kt('Document has already been checked out by ').$oCheckedOutUser->getName());
+ }
}
}else if(!is_a($oEntity, 'Folder')) {
return PEAR::raiseError(_kt('Document cannot be checked out'));
@@ -921,12 +930,29 @@ class KTBrowseBulkCheckoutAction extends KTBulkAction {
foreach($aDocuments as $sDocId){
$oDocument = Document::get($sDocId);
if(PEAR::isError($oDocument)) {
- return PEAR::raiseError(_kt('Folder documents cannot be checked out'));
+ // add message, skip document and continue
+ $this->addErrorMessage($oDocument->getName().': '.$oDocument->getMessage());
+ continue;
}
+ // Checkout document - if it is already checked out, check the owner.
+ // If the current user is the owner, then include to the download, otherwise ignore.
$res = KTDocumentUtil::checkout($oDocument, $sReason, $this->oUser);
if(PEAR::isError($res)) {
- return PEAR::raiseError($oDocument->getName().': '.$res->getMessage());
+ if($oDocument->getIsCheckedOut()){
+ $checkedOutUser = $oDocument->getCheckedOutUserID();
+ $sUserId = $_SESSION['userID'];
+
+ if($checkedOutUser != $sUserId){
+ $oCheckedOutUser = User::get($checkedOutUser);
+ $this->addErrorMessage($oDocument->getName().': '._kt('Document has already been checked out by ').$oCheckedOutUser->getName());
+ continue;
+ }
+ }
+ if($checkedOutUser != $sUserId){
+ $this->addErrorMessage($oDocument->getName().': '.$res->getMessage());
+ continue;
+ }
}
// Add document to the zip file
diff --git a/plugins/ktcore/admin/groupManagement.php b/plugins/ktcore/admin/groupManagement.php
index 765f1b1..df9cc66 100755
--- a/plugins/ktcore/admin/groupManagement.php
+++ b/plugins/ktcore/admin/groupManagement.php
@@ -645,6 +645,7 @@ class KTGroupAdminDispatcher extends KTAdminDispatcher {
$data = $res['results'];
$errors = $res['errors'];
$extra_errors = array();
+
if (is_null($data['unit']) && $data['unitadmin']) {
$extra_errors['unitadmin'] = _kt("Groups without units cannot be Unit Administrators.");
@@ -654,9 +655,21 @@ class KTGroupAdminDispatcher extends KTAdminDispatcher {
if (!PEAR::isError($oGroup)) {
$extra_errors['group_name'][] = _kt("There is already a group with that name.");
}
+
+
+ if(preg_match('/[\!\$\#\%\^\&\*]/', $data['group_name'])){
+ $extra_errors['group_name'][] = _kt("You have entered an invalid character.");
+ }
+
+ if ($data['group_name'] == ''){
+ $extra_errors['group_name'][] = _kt("You have entered an invalid name.");
+ }
+
if (!empty($errors) || !empty($extra_errors)) {
+
return $oForm->handleError(null, $extra_errors);
+
}
$this->startTransaction();
diff --git a/plugins/ktcore/admin/userManagement.php b/plugins/ktcore/admin/userManagement.php
index 6b20e27..f97bb17 100755
--- a/plugins/ktcore/admin/userManagement.php
+++ b/plugins/ktcore/admin/userManagement.php
@@ -500,11 +500,21 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
$this->errorRedirectTo('addUser', _kt("The passwords you specified do not match."), sprintf("old_search=%s&do_search=1", $old_search));
}
+ if(preg_match('/[\!\$\#\%\^\&\*]/', $username)){
+ $this->errorRedirectTo('addUser', _kt("You have entered an invalid character in your username."));
+ }
+
+ if(preg_match('/[\!\$\#\%\^\&\*]/', $name)){
+ $this->errorRedirectTo('addUser', _kt("You have entered an invalid character in your name."));
+ }
+
$dupUser =& User::getByUserName($username);
if(!PEAR::isError($dupUser)) {
$this->errorRedirectTo('addUser', _kt("A user with that username already exists"));
}
+
+
$oUser =& User::createFromArray(array(
"sUsername" => $username,
"sName" => $name,
diff --git a/plugins/ktstandard/KTBulkExportPlugin.php b/plugins/ktstandard/KTBulkExportPlugin.php
index 861793c..131aa10 100644
--- a/plugins/ktstandard/KTBulkExportPlugin.php
+++ b/plugins/ktstandard/KTBulkExportPlugin.php
@@ -65,7 +65,7 @@ class KTBulkExportAction extends KTFolderAction {
var $_sShowPermission = "ktcore.permissions.read";
function getDisplayName() {
- return _kt('Bulk Export');
+ return _kt('Bulk Download');
}
function do_main() {
diff --git a/plugins/rssplugin/RSSDedicatedDashlet.php b/plugins/rssplugin/RSSDedicatedDashlet.php
new file mode 100644
index 0000000..382c616
--- /dev/null
+++ b/plugins/rssplugin/RSSDedicatedDashlet.php
@@ -0,0 +1,79 @@
+.
+ *
+ * You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
+ * Blake Street, Observatory, 7925 South Africa. 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): ______________________________________
+ *
+ */
+
+require_once(KT_LIB_DIR . '/browse/browseutil.inc.php');
+require_once(KT_DIR. '/plugins/rssplugin/rss2array.inc.php');
+require_once(KT_DIR. '/plugins/rssplugin/KTrss.inc.php');
+
+class RSSDedicatedDashlet extends KTBaseDashlet {
+ var $oUser;
+
+ function RSSDedicatedDashlet(){
+ $oKTConfig =& KTConfig::getSingleton();
+
+ $sTitle = $oKTConfig->get('urls/dedicatedrsstitle', 'RSS');
+ $this->sTitle = $sTitle;
+ }
+
+ function is_active($oUser) {
+ $this->oUser = $oUser;
+ return true;
+ }
+
+ function render() {
+ global $main;
+ $main->requireJSResource("plugins/rssplugin/js/update.js");
+
+ $oTemplating =& KTTemplating::getSingleton();
+ $oTemplate = $oTemplating->loadTemplate('RSSPlugin/dedicated_dashlet');
+
+ $iUId = $this->oUser->getId();
+ $oKTConfig =& KTConfig::getSingleton();
+
+ $sUrl = $oKTConfig->get('urls/dedicatedrssurl', '');
+
+ // Prepare template data
+ $aTemplateData = array(
+ 'context' => $this,
+ 'url' => $sUrl,
+ 'user' => $iUId,
+ );
+
+ return $oTemplate->render($aTemplateData);
+ }
+}
+?>
diff --git a/plugins/rssplugin/RSSPlugin.php b/plugins/rssplugin/RSSPlugin.php
index ed32b76..38178d8 100644
--- a/plugins/rssplugin/RSSPlugin.php
+++ b/plugins/rssplugin/RSSPlugin.php
@@ -1,6 +1,6 @@
registerAction('folderaction', 'RSSFolderLinkAction', 'ktcore.rss.plugin.folder.link', $sFilename = null);
$this->registerAction('documentaction', 'RSSDocumentLinkAction', 'ktcore.rss.plugin.document.link', $sFilename = null);
$this->registerDashlet('RSSDashlet', 'ktcore.rss.feed.dashlet', 'RSSDashlet.php');
+ $this->registerDashlet('RSSDedicatedDashlet', 'ktcore.dedicated.rss.feed.dashlet', 'RSSDedicatedDashlet.php');
$this->registerPage('managerssfeeds', 'ManageRSSFeedsDispatcher');
require_once(KT_LIB_DIR . "/templating/templating.inc.php");
diff --git a/plugins/rssplugin/js/update.js b/plugins/rssplugin/js/update.js
index 34c5fca..72d5cf4 100644
--- a/plugins/rssplugin/js/update.js
+++ b/plugins/rssplugin/js/update.js
@@ -36,4 +36,43 @@ function GetXmlHttpObject(){
objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
return objXMLHttp;
+}
+
+function loadDedicatedFeed(user){
+ xmlHttpD=GetXmlHttpObject();
+ if (xmlHttpD===null){
+ alert ("Browser does not support HTTP Request");
+ return;
+ }
+ var feed = FEED_URL;
+ // First check if there is a feed - in the event the 'Select feed' option was selected
+ if(feed !== 'null'){
+ var url=currloc+"plugins/rssplugin/loadDedicatedFeed.inc.php";
+ url=url+"?feed="+feed;
+ url=url+"&user="+user;
+ url=url+"&sid="+Math.random();
+ xmlHttpD.onreadystatechange=stateChangedD;
+ xmlHttpD.open("GET",url,true);
+ xmlHttpD.send(null);
+ }
+}
+
+function stateChangedD(){
+ if (xmlHttpD.readyState==4 || xmlHttpD.readyState=="complete"){
+ document.getElementById("RSSDedicatedDashlet").style.display = "block";
+ document.getElementById("rssDedicatedBlock").innerHTML=xmlHttpD.responseText;
+ }else{
+ document.getElementById("RSSDedicatedDashlet").style.display = "block";
+ document.getElementById("rssDedicatedBlock").innerHTML="Loading feed...";
+ }
+}
+
+function GetXmlHttpObjectD(){
+ var objXMLHttpD=null;
+ if (window.XMLHttpRequest){
+ objXMLHttpD=new XMLHttpRequest();
+ }else if (window.ActiveXObject){
+ objXMLHttpD=new ActiveXObject("Microsoft.XMLHTTP");
+ }
+ return objXMLHttpD;
}
\ No newline at end of file
diff --git a/plugins/rssplugin/loadDedicatedFeed.inc.php b/plugins/rssplugin/loadDedicatedFeed.inc.php
new file mode 100644
index 0000000..7cd2d3d
--- /dev/null
+++ b/plugins/rssplugin/loadDedicatedFeed.inc.php
@@ -0,0 +1,93 @@
+.
+ *
+ * You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
+ * Blake Street, Observatory, 7925 South Africa. 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): ______________________________________
+ *
+ */
+ require_once('../../config/dmsDefaults.php');
+ require_once(KT_DIR. '/plugins/rssplugin/rss2array.inc.php');
+ require_once(KT_DIR. '/plugins/rssplugin/KTrss.inc.php');
+
+ $feed = $_GET["feed"];
+ $user = $_GET["user"];
+ session_start();
+
+ $sCache = _checkCache($feed);
+
+ if(!is_null($sCache)) {
+ $aRSSArray = $sCache;
+ }else{
+ $aRSSArray = rss2array($feed);
+ $_SESSION['kt_dedicated_rss'][$feed]['lastcheck'] = time();
+ $_SESSION['kt_dedicated_rss'][$feed] = $aRSSArray;
+ }
+ // Prepare response data to be passed back to page
+ $reposonse = "
".$aRSSArray[channel][title]." " .
+ "" .
+ "
";
+ for($i=0;$i".$aRSSArray[items][$i][title]."
+
+
+ ".$aRSSArray[items][$i][description]."
+
+ ";
+ }
+ $reposonse .= "
";
+
+ echo $reposonse;
+
+ function _checkCache($feed) {
+ session_start();
+ $iLastCheck = $_SESSION['kt_dedicated_rss'][$feed]['lastcheck'];
+
+ if (empty($iLastCheck)) {
+ return;
+ }
+ $sStoredFeed = $_SESSION['kt_dedicated_rss'][$feed];
+ if (empty($sStoredFeed)) {
+ $now = time();
+ $diff = $now - $iLastCheck;
+ if ($diff > (5*60)) {
+ return;
+ }
+ }
+ $now = time();
+ $diff = $now - $iLastCheck;
+ if ($diff > (5*60)) {
+ return;
+ }
+ return $sStoredFeed;
+}
+?>
diff --git a/plugins/rssplugin/templates/RSSPlugin/dedicated_dashlet.smarty b/plugins/rssplugin/templates/RSSPlugin/dedicated_dashlet.smarty
new file mode 100644
index 0000000..2a16da4
--- /dev/null
+++ b/plugins/rssplugin/templates/RSSPlugin/dedicated_dashlet.smarty
@@ -0,0 +1,18 @@
+{literal}
+
+
+
+{/literal}
+
+{if $url != ''}
+
+
+{/if}
\ No newline at end of file
diff --git a/sql/mysql/install/structure.sql b/sql/mysql/install/structure.sql
index 291f6ea..747b7d1 100644
--- a/sql/mysql/install/structure.sql
+++ b/sql/mysql/install/structure.sql
@@ -1,34 +1,34 @@
--
-- $Id$
---
+--
-- KnowledgeTree Open Source Edition
-- Document Management Made Simple
-- Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
---
+--
-- 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 The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
-- Blake Street, Observatory, 7925 South Africa. 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. -- Contributor( s): ______________________________________
--
-- MySQL dump 10.11
@@ -204,13 +204,13 @@ CREATE TABLE `discussion_comments` (
--
CREATE TABLE `discussion_threads` (
- `id` int(11) NOT NULL default '0',
- `document_id` int(11) NOT NULL default '0',
- `first_comment_id` int(11) NOT NULL default '0',
- `last_comment_id` int(11) NOT NULL default '0',
+ `id` int(11) NOT NULL,
+ `document_id` int(11) NOT NULL,
+ `first_comment_id` int(11) NULL,
+ `last_comment_id` int(11) NULL,
`views` int(11) NOT NULL default '0',
`replies` int(11) NOT NULL default '0',
- `creator_id` int(11) NOT NULL default '0',
+ `creator_id` int(11) NOT NULL,
`close_reason` mediumtext NOT NULL,
`close_metadata_version` int(11) NOT NULL default '0',
`state` int(1) NOT NULL default '0',
diff --git a/templates/ktcore/manage_help.smarty b/templates/ktcore/manage_help.smarty
index 96881b0..e87c0fb 100644
--- a/templates/ktcore/manage_help.smarty
+++ b/templates/ktcore/manage_help.smarty
@@ -1,10 +1,10 @@
{i18n}Current help assignments{/i18n}
-{i18n}To customise a help file, please visit that file
-via the help system and click on customise this help file .{/i18n}
+{i18n}To customize a help file, please visit that file
+via the help system and click on customize this help file .{/i18n}
{ if $helpreplacements }
-{i18n}Existing customised help pages{/i18n}
+{i18n}Existing customized help pages{/i18n}
@@ -30,7 +30,7 @@ via the help system and click on customise this help file .{/i18
{else}
-{i18n}No help files have been customised.{/i18n}
+{i18n}No help files have been customized.{/i18n}
{ /if }
diff --git a/templates/ktstandard/disclaimers/manage_disclaimers.smarty b/templates/ktstandard/disclaimers/manage_disclaimers.smarty
index 765d769..8767294 100644
--- a/templates/ktstandard/disclaimers/manage_disclaimers.smarty
+++ b/templates/ktstandard/disclaimers/manage_disclaimers.smarty
@@ -1,6 +1,6 @@
{i18n}Disclaimers{/i18n}
-{i18n}Please select a disclaimer to customise.{/i18n}
+{i18n}Please select a disclaimer to customize.{/i18n}
{i18n}Existing disclaimers{/i18n}
diff --git a/thirdparty/pear/HTTP/WebDAV/Server.php b/thirdparty/pear/HTTP/WebDAV/Server.php
deleted file mode 100755
index 8e200c9..0000000
--- a/thirdparty/pear/HTTP/WebDAV/Server.php
+++ /dev/null
@@ -1,1964 +0,0 @@
- |
-// | Christian Stocker |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-require_once "HTTP/WebDAV/Tools/_parse_propfind.php";
-require_once "HTTP/WebDAV/Tools/_parse_proppatch.php";
-require_once "HTTP/WebDAV/Tools/_parse_lockinfo.php";
-
-
-
-/**
- * Virtual base class for implementing WebDAV servers
- *
- * WebDAV server base class, needs to be extended to do useful work
- *
- * @package HTTP_WebDAV_Server
- * @author Hartmut Holzgraefe
- * @version 0.99.1dev
- */
-class HTTP_WebDAV_Server
-{
- // {{{ Member Variables
-
- /**
- * complete URI for this request
- *
- * @var string
- */
- var $uri;
-
-
- /**
- * base URI for this request
- *
- * @var string
- */
- var $base_uri;
-
-
- /**
- * URI path for this request
- *
- * @var string
- */
- var $path;
-
- /**
- * Realm string to be used in authentification popups
- *
- * @var string
- */
- var $http_auth_realm = "PHP WebDAV";
-
- /**
- * String to be used in "X-Dav-Powered-By" header
- *
- * @var string
- */
- var $dav_powered_by = "";
-
- /**
- * Remember parsed If: (RFC2518/9.4) header conditions
- *
- * @var array
- */
- var $_if_header_uris = array();
-
- /**
- * HTTP response status/message
- *
- * @var string
- */
- var $_http_status = "200 OK";
-
- /**
- * encoding of property values passed in
- *
- * @var string
- */
- var $_prop_encoding = "utf-8";
-
- // }}}
-
- // {{{ Constructor
-
- /**
- * Constructor
- *
- * @param void
- */
- function HTTP_WebDAV_Server()
- {
- // PHP messages destroy XML output -> switch them off
- ini_set("display_errors", 0);
- }
-
- // }}}
-
- // {{{ ServeRequest()
- /**
- * Serve WebDAV HTTP request
- *
- * dispatch WebDAV HTTP request to the apropriate method handler
- *
- * @param void
- * @return void
- */
- function ServeRequest()
- {
- // default uri is the complete request uri
- $uri = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:");
- $uri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
-
- $this->base_uri = $uri;
- $this->uri = $uri . $_SERVER["PATH_INFO"];
-
- // identify ourselves
- if (empty($this->dav_powered_by)) {
- header("X-Dav-Powered-By: PHP class: ".get_class($this));
- } else {
- header("X-Dav-Powered-By: ".$this->dav_powered_by );
- }
-
- // check authentication
- if (!$this->_check_auth()) {
- // RFC2518 says we must use Digest instead of Basic
- // but Microsoft Clients do not support Digest
- // and we don't support NTLM and Kerberos
- // so we are stuck with Basic here
- header('WWW-Authenticate: Basic realm="'.($this->http_auth_realm).'"');
-
- // Windows seems to require this being the last header sent
- // (changed according to PECL bug #3138)
- $this->http_status('401 Unauthorized');
-
- return;
- }
-
- // check
- if(! $this->_check_if_header_conditions()) {
- $this->http_status("412 Precondition failed");
- return;
- }
-
- // set path
- $this->path = $this->_urldecode($_SERVER["PATH_INFO"]);
- if (!strlen($this->path)) {
- if ($_SERVER["REQUEST_METHOD"] == "GET") {
- // redirect clients that try to GET a collection
- // WebDAV clients should never try this while
- // regular HTTP clients might ...
- header("Location: ".$this->base_uri."/");
- exit;
- } else {
- // if a WebDAV client didn't give a path we just assume '/'
- $this->path = "/";
- }
- }
-
- if(ini_get("magic_quotes_gpc")) {
- $this->path = stripslashes($this->path);
- }
-
-
- // detect requested method names
- $method = strtolower($_SERVER["REQUEST_METHOD"]);
- $wrapper = "http_".$method;
-
- // activate HEAD emulation by GET if no HEAD method found
- if ($method == "head" && !method_exists($this, "head")) {
- $method = "get";
- }
-
- if (method_exists($this, $wrapper) && ($method == "options" || method_exists($this, $method))) {
- $this->$wrapper(); // call method by name
- } else { // method not found/implemented
- if ($_SERVER["REQUEST_METHOD"] == "LOCK") {
- $this->http_status("412 Precondition failed");
- } else {
- $this->http_status("405 Method not allowed");
- header("Allow: ".join(", ", $this->_allow())); // tell client what's allowed
- }
- }
- }
-
- // }}}
-
- // {{{ abstract WebDAV methods
-
- // {{{ GET()
- /**
- * GET implementation
- *
- * overload this method to retrieve resources from your server
- *
- *
- *
- * @abstract
- * @param array &$params Array of input and output parameters
- * input
- * output
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function GET(&$params)
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // {{{ PUT()
- /**
- * PUT implementation
- *
- * PUT implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function PUT()
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // {{{ COPY()
-
- /**
- * COPY implementation
- *
- * COPY implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function COPY()
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // {{{ MOVE()
-
- /**
- * MOVE implementation
- *
- * MOVE implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function MOVE()
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // {{{ DELETE()
-
- /**
- * DELETE implementation
- *
- * DELETE implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function DELETE()
- {
- // dummy entry for PHPDoc
- }
- */
- // }}}
-
- // {{{ PROPFIND()
-
- /**
- * PROPFIND implementation
- *
- * PROPFIND implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function PROPFIND()
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // {{{ PROPPATCH()
-
- /**
- * PROPPATCH implementation
- *
- * PROPPATCH implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function PROPPATCH()
- {
- // dummy entry for PHPDoc
- }
- */
- // }}}
-
- // {{{ LOCK()
-
- /**
- * LOCK implementation
- *
- * LOCK implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function LOCK()
- {
- // dummy entry for PHPDoc
- }
- */
- // }}}
-
- // {{{ UNLOCK()
-
- /**
- * UNLOCK implementation
- *
- * UNLOCK implementation
- *
- * @abstract
- * @param array &$params
- * @returns int HTTP-Statuscode
- */
-
- /* abstract
- function UNLOCK()
- {
- // dummy entry for PHPDoc
- }
- */
- // }}}
-
- // }}}
-
- // {{{ other abstract methods
-
- // {{{ check_auth()
-
- /**
- * check authentication
- *
- * overload this method to retrieve and confirm authentication information
- *
- * @abstract
- * @param string type Authentication type, e.g. "basic" or "digest"
- * @param string username Transmitted username
- * @param string passwort Transmitted password
- * @returns bool Authentication status
- */
-
- /* abstract
- function checkAuth($type, $username, $password)
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // {{{ checklock()
-
- /**
- * check lock status for a resource
- *
- * overload this method to return shared and exclusive locks
- * active for this resource
- *
- * @abstract
- * @param string resource Resource path to check
- * @returns array An array of lock entries each consisting
- * of 'type' ('shared'/'exclusive'), 'token' and 'timeout'
- */
-
- /* abstract
- function checklock($resource)
- {
- // dummy entry for PHPDoc
- }
- */
-
- // }}}
-
- // }}}
-
- // {{{ WebDAV HTTP method wrappers
-
- // {{{ http_OPTIONS()
-
- /**
- * OPTIONS method handler
- *
- * The OPTIONS method handler creates a valid OPTIONS reply
- * including Dav: and Allowed: heaers
- * based on the implemented methods found in the actual instance
- *
- * @param void
- * @return void
- */
- function http_OPTIONS()
- {
- // Microsoft clients default to the Frontpage protocol
- // unless we tell them to use WebDAV
- header("MS-Author-Via: DAV");
-
- // get allowed methods
- $allow = $this->_allow();
-
- // dav header
- $dav = array(1); // assume we are always dav class 1 compliant
- if (isset($allow['LOCK'])) {
- $dav[] = 2; // dav class 2 requires that locking is supported
- }
-
- // tell clients what we found
- $this->http_status("200 OK");
- header("DAV: " .join("," , $dav));
- header("Allow: ".join(", ", $allow));
-
- header("Content-length: 0");
- }
-
- // }}}
-
-
- // {{{ http_PROPFIND()
-
- /**
- * PROPFIND method handler
- *
- * @param void
- * @return void
- */
- function http_PROPFIND()
- {
- $options = Array();
- $options["path"] = $this->path;
-
- // search depth from header (default is "infinity)
- if (isset($_SERVER['HTTP_DEPTH'])) {
- $options["depth"] = $_SERVER["HTTP_DEPTH"];
- } else {
- $options["depth"] = "infinity";
- }
-
- // analyze request payload
- $propinfo = new _parse_propfind("php://input");
- if (!$propinfo->success) {
- $this->http_status("400 Error");
- return;
- }
- $options['props'] = $propinfo->props;
-
- // call user handler
- if (!$this->PROPFIND($options, $files)) {
- $this->http_status("404 Not Found");
- return;
- }
-
- // collect namespaces here
- $ns_hash = array();
-
- // Microsoft Clients need this special namespace for date and time values
- $ns_defs = "xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\"";
-
- // now we loop over all returned file entries
- foreach($files["files"] as $filekey => $file) {
-
- // nothing to do if no properties were returend for a file
- if (!isset($file["props"]) || !is_array($file["props"])) {
- continue;
- }
-
- // now loop over all returned properties
- foreach($file["props"] as $key => $prop) {
- // as a convenience feature we do not require that user handlers
- // restrict returned properties to the requested ones
- // here we strip all unrequested entries out of the response
-
- switch($options['props']) {
- case "all":
- // nothing to remove
- break;
-
- case "names":
- // only the names of all existing properties were requested
- // so we remove all values
- unset($files["files"][$filekey]["props"][$key]["val"]);
- break;
-
- default:
- $found = false;
-
- // search property name in requested properties
- foreach((array)$options["props"] as $reqprop) {
- if ( $reqprop["name"] == $prop["name"]
- && $reqprop["xmlns"] == $prop["ns"]) {
- $found = true;
- break;
- }
- }
-
- // unset property and continue with next one if not found/requested
- if (!$found) {
- $files["files"][$filekey]["props"][$key]="";
- continue(2);
- }
- break;
- }
-
- // namespace handling
- if (empty($prop["ns"])) continue; // no namespace
- $ns = $prop["ns"];
- if ($ns == "DAV:") continue; // default namespace
- if (isset($ns_hash[$ns])) continue; // already known
-
- // register namespace
- $ns_name = "ns".(count($ns_hash) + 1);
- $ns_hash[$ns] = $ns_name;
- $ns_defs .= " xmlns:$ns_name=\"$ns\"";
- }
-
- // we also need to add empty entries for properties that were requested
- // but for which no values where returned by the user handler
- if (is_array($options['props'])) {
- foreach($options["props"] as $reqprop) {
- if($reqprop['name']=="") continue; // skip empty entries
-
- $found = false;
-
- // check if property exists in result
- foreach($file["props"] as $prop) {
- if ( $reqprop["name"] == $prop["name"]
- && $reqprop["xmlns"] == $prop["ns"]) {
- $found = true;
- break;
- }
- }
-
- if (!$found) {
- if($reqprop["xmlns"]==="DAV:" && $reqprop["name"]==="lockdiscovery") {
- // lockdiscovery is handled by the base class
- $files["files"][$filekey]["props"][]
- = $this->mkprop("DAV:",
- "lockdiscovery" ,
- $this->lockdiscovery($files["files"][$filekey]['path']));
- } else {
- // add empty value for this property
- $files["files"][$filekey]["noprops"][] =
- $this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
-
- // register property namespace if not known yet
- if ($reqprop["xmlns"] != "DAV:" && !isset($ns_hash[$reqprop["xmlns"]])) {
- $ns_name = "ns".(count($ns_hash) + 1);
- $ns_hash[$reqprop["xmlns"]] = $ns_name;
- $ns_defs .= " xmlns:$ns_name=\"$reqprop[xmlns]\"";
- }
- }
- }
- }
- }
- }
-
- // now we generate the reply header ...
- $this->http_status("207 Multi-Status");
- header('Content-Type: text/xml; charset="utf-8"');
-
- // ... and payload
- echo "\n";
- echo "\n";
-
- foreach($files["files"] as $file) {
- // ignore empty or incomplete entries
- if(!is_array($file) || empty($file) || !isset($file["path"])) continue;
- $path = $file['path'];
- if(!is_string($path) || $path==="") continue;
-
- echo " \n";
-
- $href = $this->_slashify($this->_mergePathes($_SERVER['SCRIPT_NAME'], $path));
-
- echo " $href \n";
-
- // report all found properties and their values (if any)
- if (isset($file["props"]) && is_array($file["props"])) {
- echo " \n";
- echo " \n";
-
- foreach($file["props"] as $key => $prop) {
-
- if (!is_array($prop)) continue;
- if (!isset($prop["name"])) continue;
-
- if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) {
- // empty properties (cannot use empty() for check as "0" is a legal value here)
- if($prop["ns"]=="DAV:") {
- echo " \n";
- } else if(!empty($prop["ns"])) {
- echo " <".$ns_hash[$prop["ns"]].":$prop[name]/>\n";
- } else {
- echo " <$prop[name] xmlns=\"\"/>";
- }
- } else if ($prop["ns"] == "DAV:") {
- // some WebDAV properties need special treatment
- switch ($prop["name"]) {
- case "creationdate":
- echo " "
- . gmdate("Y-m-d\\TH:i:s\\Z",$prop['val'])
- . " \n";
- break;
- case "getlastmodified":
- echo " "
- . gmdate("D, d M Y H:i:s ", $prop['val'])
- . "GMT \n";
- break;
- case "resourcetype":
- echo " \n";
- break;
- case "supportedlock":
- echo " $prop[val] \n";
- break;
- case "lockdiscovery":
- echo " \n";
- echo $prop["val"];
- echo " \n";
- break;
- default:
- echo " "
- . $this->_prop_encode(htmlspecialchars($prop['val']))
- . " \n";
- break;
- }
- } else {
- // properties from namespaces != "DAV:" or without any namespace
- if ($prop["ns"]) {
- echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>"
- . $this->_prop_encode(htmlspecialchars($prop['val']))
- . "" . $ns_hash[$prop["ns"]] . ":$prop[name]>\n";
- } else {
- echo " <$prop[name] xmlns=\"\">"
- . $this->_prop_encode(htmlspecialchars($prop['val']))
- . "$prop[name]>\n";
- }
- }
- }
-
- echo " \n";
- echo " HTTP/1.1 200 OK \n";
- echo " \n";
- }
-
- // now report all properties requested but not found
- if (isset($file["noprops"])) {
- echo " \n";
- echo " \n";
-
- foreach($file["noprops"] as $key => $prop) {
- if ($prop["ns"] == "DAV:") {
- echo " \n";
- } else if ($prop["ns"] == "") {
- echo " <$prop[name] xmlns=\"\"/>\n";
- } else {
- echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]/>\n";
- }
- }
-
- echo " \n";
- echo " HTTP/1.1 404 Not Found \n";
- echo " \n";
- }
-
- echo " \n";
- }
-
- echo " \n";
- }
-
-
- // }}}
-
- // {{{ http_PROPPATCH()
-
- /**
- * PROPPATCH method handler
- *
- * @param void
- * @return void
- */
- function http_PROPPATCH()
- {
- if($this->_check_lock_status($this->path)) {
- $options = Array();
- $options["path"] = $this->path;
-
- $propinfo = new _parse_proppatch("php://input");
-
- if (!$propinfo->success) {
- $this->http_status("400 Error");
- return;
- }
-
- $options['props'] = $propinfo->props;
-
- $responsedescr = $this->PROPPATCH($options);
-
- $this->http_status("207 Multi-Status");
- header('Content-Type: text/xml; charset="utf-8"');
-
- echo "\n";
-
- echo "\n";
- echo " \n";
- echo " ".$this->_urlencode($this->_mergePathes($_SERVER["SCRIPT_NAME"], $this->path))." \n";
-
- foreach($options["props"] as $prop) {
- echo " \n";
- echo " <$prop[name] xmlns=\"$prop[ns]\"/> \n";
- echo " HTTP/1.1 $prop[status] \n";
- echo " \n";
- }
-
- if ($responsedescr) {
- echo " ".
- $this->_prop_encode(htmlspecialchars($responsedescr)).
- " \n";
- }
-
- echo " \n";
- echo " \n";
- } else {
- $this->http_status("423 Locked");
- }
- }
-
- // }}}
-
-
- // {{{ http_MKCOL()
-
- /**
- * MKCOL method handler
- *
- * @param void
- * @return void
- */
- function http_MKCOL()
- {
- $options = Array();
- $options["path"] = $this->path;
-
- $stat = $this->MKCOL($options);
-
- $this->http_status($stat);
- }
-
- // }}}
-
-
- // {{{ http_GET()
-
- /**
- * GET method handler
- *
- * @param void
- * @returns void
- */
- function http_GET()
- {
- // TODO check for invalid stream
- $options = Array();
- $options["path"] = $this->path;
-
- $this->_get_ranges($options);
-
- if (true === ($status = $this->GET($options))) {
- if (!headers_sent()) {
- $status = "200 OK";
-
- if (!isset($options['mimetype'])) {
- $options['mimetype'] = "application/octet-stream";
- }
- header("Content-type: $options[mimetype]");
-
- if (isset($options['mtime'])) {
- header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT");
- }
-
- if (isset($options['stream'])) {
- // GET handler returned a stream
- if (!empty($options['ranges']) && (0===fseek($options['stream'], 0, SEEK_SET))) {
- // partial request and stream is seekable
-
- if (count($options['ranges']) === 1) {
- $range = $options['ranges'][0];
-
- if (isset($range['start'])) {
- fseek($options['stream'], $range['start'], SEEK_SET);
- if (feof($options['stream'])) {
- $this->http_status("416 Requested range not satisfiable");
- exit;
- }
-
- if (isset($range['end'])) {
- $size = $range['end']-$range['start']+1;
- $this->http_status("206 partial");
- header("Content-length: $size");
- header("Content-range: $range[start]-$range[end]/"
- . (isset($options['size']) ? $options['size'] : "*"));
- while ($size && !feof($options['stream'])) {
- $buffer = fread($options['stream'], 4096);
- $size -= strlen($buffer);
- echo $buffer;
- }
- } else {
- $this->http_status("206 partial");
- if (isset($options['size'])) {
- header("Content-length: ".($options['size'] - $range['start']));
- header("Content-range: $start-$end/"
- . (isset($options['size']) ? $options['size'] : "*"));
- }
- fpassthru($options['stream']);
- }
- } else {
- header("Content-length: ".$range['last']);
- fseek($options['stream'], -$range['last'], SEEK_END);
- fpassthru($options['stream']);
- }
- } else {
- $this->_multipart_byterange_header(); // init multipart
- foreach ($options['ranges'] as $range) {
- // TODO what if size unknown? 500?
- if (isset($range['start'])) {
- $from = $range['start'];
- $to = !empty($range['end']) ? $range['end'] : $options['size']-1;
- } else {
- $from = $options['size'] - $range['last']-1;
- $to = $options['size'] -1;
- }
- $total = isset($options['size']) ? $options['size'] : "*";
- $size = $to - $from + 1;
- $this->_multipart_byterange_header($options['mimetype'], $from, $to, $total);
-
-
- fseek($options['stream'], $start, SEEK_SET);
- while ($size && !feof($options['stream'])) {
- $buffer = fread($options['stream'], 4096);
- $size -= strlen($buffer);
- echo $buffer;
- }
- }
- $this->_multipart_byterange_header(); // end multipart
- }
- } else {
- // normal request or stream isn't seekable, return full content
- if (isset($options['size'])) {
- header("Content-length: ".$options['size']);
- }
- fpassthru($options['stream']);
- return; // no more headers
- }
- } elseif (isset($options['data'])) {
- if (is_array($options['data'])) {
- // reply to partial request
- } else {
- header("Content-length: ".strlen($options['data']));
- echo $options['data'];
- }
- }
- }
- }
-
- if (!headers_sent()) {
- if (false === $status) {
- $this->http_status("404 not found");
- }
-
- // TODO: check setting of headers in various code pathes above
- $this->http_status("$status");
- }
- }
-
-
- /**
- * parse HTTP Range: header
- *
- * @param array options array to store result in
- * @return void
- */
- function _get_ranges(&$options)
- {
- // process Range: header if present
- if (isset($_SERVER['HTTP_RANGE'])) {
-
- // we only support standard "bytes" range specifications for now
- if (ereg("bytes[[:space:]]*=[[:space:]]*(.+)", $_SERVER['HTTP_RANGE'], $matches)) {
- $options["ranges"] = array();
-
- // ranges are comma separated
- foreach (explode(",", $matches[1]) as $range) {
- // ranges are either from-to pairs or just end positions
- list($start, $end) = explode("-", $range);
- $options["ranges"][] = ($start==="")
- ? array("last"=>$end)
- : array("start"=>$start, "end"=>$end);
- }
- }
- }
- }
-
- /**
- * generate separator headers for multipart response
- *
- * first and last call happen without parameters to generate
- * the initial header and closing sequence, all calls inbetween
- * require content mimetype, start and end byte position and
- * optionaly the total byte length of the requested resource
- *
- * @param string mimetype
- * @param int start byte position
- * @param int end byte position
- * @param int total resource byte size
- */
- function _multipart_byterange_header($mimetype = false, $from = false, $to=false, $total=false)
- {
- if ($mimetype === false) {
- if (!isset($this->multipart_separator)) {
- // initial
-
- // a little naive, this sequence *might* be part of the content
- // but it's really not likely and rather expensive to check
- $this->multipart_separator = "SEPARATOR_".md5(microtime());
-
- // generate HTTP header
- header("Content-type: multipart/byteranges; boundary=".$this->multipart_separator);
- } else {
- // final
-
- // generate closing multipart sequence
- echo "\n--{$this->multipart_separator}--";
- }
- } else {
- // generate separator and header for next part
- echo "\n--{$this->multipart_separator}\n";
- echo "Content-type: $mimetype\n";
- echo "Content-range: $from-$to/". ($total === false ? "*" : $total);
- echo "\n\n";
- }
- }
-
-
-
- // }}}
-
- // {{{ http_HEAD()
-
- /**
- * HEAD method handler
- *
- * @param void
- * @return void
- */
- function http_HEAD()
- {
- $status = false;
- $options = Array();
- $options["path"] = $this->path;
-
- if (method_exists($this, "HEAD")) {
- $status = $this->head($options);
- } else if (method_exists($this, "GET")) {
- ob_start();
- $status = $this->GET($options);
- ob_end_clean();
- }
-
- if($status===true) $status = "200 OK";
- if($status===false) $status = "404 Not found";
-
- $this->http_status($status);
- }
-
- // }}}
-
- // {{{ http_PUT()
-
- /**
- * PUT method handler
- *
- * @param void
- * @return void
- */
- function http_PUT()
- {
- if ($this->_check_lock_status($this->path)) {
- $options = Array();
- $options["path"] = $this->path;
- $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
-
- // get the Content-type
- if (isset($_SERVER["CONTENT_TYPE"])) {
- // for now we do not support any sort of multipart requests
- if (!strncmp($_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
- $this->http_status("501 not implemented");
- echo "The service does not support mulipart PUT requests";
- return;
- }
- $options["content_type"] = $_SERVER["CONTENT_TYPE"];
- } else {
- // default content type if none given
- $options["content_type"] = "application/octet-stream";
- }
-
- /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
- ignore any Content-* (e.g. Content-Range) headers that it
- does not understand or implement and MUST return a 501
- (Not Implemented) response in such cases."
- */
- foreach ($_SERVER as $key => $val) {
- if (strncmp($key, "HTTP_CONTENT", 11)) continue;
- switch ($key) {
- case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
- // TODO support this if ext/zlib filters are available
- $this->http_status("501 not implemented");
- echo "The service does not support '$val' content encoding";
- return;
-
- case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
- // we assume it is not critical if this one is ignored
- // in the actual PUT implementation ...
- $options["content_language"] = $value;
- break;
-
- case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
- /* The meaning of the Content-Location header in PUT
- or POST requests is undefined; servers are free
- to ignore it in those cases. */
- break;
-
- case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
- // single byte range requests are supported
- // the header format is also specified in RFC 2616 14.16
- // TODO we have to ensure that implementations support this or send 501 instead
- if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
- $this->http_status("400 bad request");
- echo "The service does only support single byte ranges";
- return;
- }
-
- $range = array("start"=>$matches[1], "end"=>$matches[2]);
- if (is_numeric($matches[3])) {
- $range["total_length"] = $matches[3];
- }
- $option["ranges"][] = $range;
-
- // TODO make sure the implementation supports partial PUT
- // this has to be done in advance to avoid data being overwritten
- // on implementations that do not support this ...
- break;
-
- case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
- // TODO: maybe we can just pretend here?
- $this->http_status("501 not implemented");
- echo "The service does not support content MD5 checksum verification";
- return;
-
- default:
- // any other unknown Content-* headers
- $this->http_status("501 not implemented");
- echo "The service does not support '$key'";
- return;
- }
- }
-
- $options["stream"] = fopen("php://input", "r");
-
- $stat = $this->PUT($options);
-
- if ($stat === false) {
- $stat = "403 Forbidden";
- } else if (is_resource($stat) && get_resource_type($stat) == "stream") {
- $stream = $stat;
-
- $stat = $options["new"] ? "201 Created" : "204 No Content";
-
- if (!empty($options["ranges"])) {
- // TODO multipart support is missing (see also above)
- if (0 == fseek($stream, $range[0]["start"], SEEK_SET)) {
- $length = $range[0]["end"]-$range[0]["start"]+1;
- if (!fwrite($stream, fread($options["stream"], $length))) {
- $stat = "403 Forbidden";
- }
- } else {
- $stat = "403 Forbidden";
- }
- } else {
- while (!feof($options["stream"])) {
- if (false === fwrite($stream, fread($options["stream"], 4096))) {
- $stat = "403 Forbidden";
- break;
- }
- }
- }
-
- fclose($stream);
- }
-
- $this->http_status($stat);
- } else {
- $this->http_status("423 Locked");
- }
- }
-
- // }}}
-
-
- // {{{ http_DELETE()
-
- /**
- * DELETE method handler
- *
- * @param void
- * @return void
- */
- function http_DELETE()
- {
- // check RFC 2518 Section 9.2, last paragraph
- if (isset($_SERVER["HTTP_DEPTH"])) {
- if ($_SERVER["HTTP_DEPTH"] != "infinity") {
- $this->http_status("400 Bad Request");
- return;
- }
- }
-
- // check lock status
- if ($this->_check_lock_status($this->path)) {
- // ok, proceed
- $options = Array();
- $options["path"] = $this->path;
-
- $stat = $this->DELETE($options);
-
- $this->http_status($stat);
- } else {
- // sorry, its locked
- $this->http_status("423 Locked");
- }
- }
-
- // }}}
-
- // {{{ http_COPY()
-
- /**
- * COPY method handler
- *
- * @param void
- * @return void
- */
- function http_COPY()
- {
- // no need to check source lock status here
- // destination lock status is always checked by the helper method
- $this->_copymove("copy");
- }
-
- // }}}
-
- // {{{ http_MOVE()
-
- /**
- * MOVE method handler
- *
- * @param void
- * @return void
- */
- function http_MOVE()
- {
- if ($this->_check_lock_status($this->path)) {
- // destination lock status is always checked by the helper method
- $this->_copymove("move");
- } else {
- $this->http_status("423 Locked");
- }
- }
-
- // }}}
-
-
- // {{{ http_LOCK()
-
- /**
- * LOCK method handler
- *
- * @param void
- * @return void
- */
- function http_LOCK()
- {
- $options = Array();
- $options["path"] = $this->path;
-
- if (isset($_SERVER['HTTP_DEPTH'])) {
- $options["depth"] = $_SERVER["HTTP_DEPTH"];
- } else {
- $options["depth"] = "infinity";
- }
-
- if (isset($_SERVER["HTTP_TIMEOUT"])) {
- $options["timeout"] = explode(",", $_SERVER["HTTP_TIMEOUT"]);
- }
-
- if(empty($_SERVER['CONTENT_LENGTH']) && !empty($_SERVER['HTTP_IF'])) {
- // check if locking is possible
- if(!$this->_check_lock_status($this->path)) {
- $this->http_status("423 Locked");
- return;
- }
-
- // refresh lock
- $options["update"] = substr($_SERVER['HTTP_IF'], 2, -2);
- $stat = $this->LOCK($options);
- } else {
- // extract lock request information from request XML payload
- $lockinfo = new _parse_lockinfo("php://input");
- if (!$lockinfo->success) {
- $this->http_status("400 bad request");
- }
-
- // check if locking is possible
- if(!$this->_check_lock_status($this->path, $lockinfo->lockscope === "shared")) {
- $this->http_status("423 Locked");
- return;
- }
-
- // new lock
- $options["scope"] = $lockinfo->lockscope;
- $options["type"] = $lockinfo->locktype;
- $options["owner"] = $lockinfo->owner;
-
- $options["locktoken"] = $this->_new_locktoken();
-
- $stat = $this->LOCK($options);
- }
-
- if(is_bool($stat)) {
- $http_stat = $stat ? "200 OK" : "423 Locked";
- } else {
- $http_stat = $stat;
- }
-
- $this->http_status($http_stat);
-
- if ($http_stat{0} == 2) { // 2xx states are ok
- if($options["timeout"]) {
- // more than a million is considered an absolute timestamp
- // less is more likely a relative value
- if($options["timeout"]>1000000) {
- $timeout = "Second-".($options['timeout']-time());
- } else {
- $timeout = "Second-$options[timeout]";
- }
- } else {
- $timeout = "Infinite";
- }
-
- header('Content-Type: text/xml; charset="utf-8"');
- header("Lock-Token: <$options[locktoken]>");
- echo "\n";
- echo "\n";
- echo " \n";
- echo " \n";
- echo " \n";
- echo " \n";
- echo " $options[depth] \n";
- echo " $options[owner] \n";
- echo " $timeout \n";
- echo " $options[locktoken] \n";
- echo " \n";
- echo " \n";
- echo " \n\n";
- }
- }
-
-
- // }}}
-
- // {{{ http_UNLOCK()
-
- /**
- * UNLOCK method handler
- *
- * @param void
- * @return void
- */
- function http_UNLOCK()
- {
- $options = Array();
- $options["path"] = $this->path;
-
- if (isset($_SERVER['HTTP_DEPTH'])) {
- $options["depth"] = $_SERVER["HTTP_DEPTH"];
- } else {
- $options["depth"] = "infinity";
- }
-
- // strip surrounding <>
- $options["token"] = substr(trim($_SERVER["HTTP_LOCK_TOKEN"]), 1, -1);
-
- // call user method
- $stat = $this->UNLOCK($options);
-
- $this->http_status($stat);
- }
-
- // }}}
-
- // }}}
-
- // {{{ _copymove()
-
- function _copymove($what)
- {
- $options = Array();
- $options["path"] = $this->path;
-
- if (isset($_SERVER["HTTP_DEPTH"])) {
- $options["depth"] = $_SERVER["HTTP_DEPTH"];
- } else {
- $options["depth"] = "infinity";
- }
-
- extract(parse_url($_SERVER["HTTP_DESTINATION"]));
- $path = urldecode($path);
- $http_host = $host;
- if (isset($port) && $port != 80)
- $http_host.= ":$port";
-
- $http_header_host = ereg_replace(":80$", "", $_SERVER["HTTP_HOST"]);
-
- if ($http_host == $http_header_host &&
- !strncmp($_SERVER["SCRIPT_NAME"], $path,
- strlen($_SERVER["SCRIPT_NAME"]))) {
- $options["dest"] = substr($path, strlen($_SERVER["SCRIPT_NAME"]));
- if (!$this->_check_lock_status($options["dest"])) {
- $this->http_status("423 Locked");
- return;
- }
-
- } else {
- $options["dest_url"] = $_SERVER["HTTP_DESTINATION"];
- }
-
- // see RFC 2518 Sections 9.6, 8.8.4 and 8.9.3
- if (isset($_SERVER["HTTP_OVERWRITE"])) {
- $options["overwrite"] = $_SERVER["HTTP_OVERWRITE"] == "T";
- } else {
- $options["overwrite"] = true;
- }
-
- $stat = $this->$what($options);
- $this->http_status($stat);
- }
-
- // }}}
-
- // {{{ _allow()
-
- /**
- * check for implemented HTTP methods
- *
- * @param void
- * @return array something
- */
- function _allow()
- {
- // OPTIONS is always there
- $allow = array("OPTIONS" =>"OPTIONS");
-
- // all other METHODS need both a http_method() wrapper
- // and a method() implementation
- // the base class supplies wrappers only
- foreach(get_class_methods($this) as $method) {
- if (!strncmp("http_", $method, 5)) {
- $method = strtoupper(substr($method, 5));
- if (method_exists($this, $method)) {
- $allow[$method] = $method;
- }
- }
- }
-
- // we can emulate a missing HEAD implemetation using GET
- if (isset($allow["GET"]))
- $allow["HEAD"] = "HEAD";
-
- // no LOCK without checklok()
- if (!method_exists($this, "checklock")) {
- unset($allow["LOCK"]);
- unset($allow["UNLOCK"]);
- }
-
- return $allow;
- }
-
- // }}}
-
- /**
- * helper for property element creation
- *
- * @param string XML namespace (optional)
- * @param string property name
- * @param string property value
- * @return array property array
- */
- function mkprop()
- {
- $args = func_get_args();
- if (count($args) == 3) {
- return array("ns" => $args[0],
- "name" => $args[1],
- "val" => $args[2]);
- } else {
- return array("ns" => "DAV:",
- "name" => $args[0],
- "val" => $args[1]);
- }
- }
-
- // {{{ _check_auth
-
- /**
- * check authentication if check is implemented
- *
- * @param void
- * @return bool true if authentication succeded or not necessary
- */
- function _check_auth()
- {
- if (method_exists($this, "checkAuth")) {
- // PEAR style method name
- return $this->checkAuth(@$_SERVER["AUTH_TYPE"],
- @$_SERVER["PHP_AUTH_USER"],
- @$_SERVER["PHP_AUTH_PW"]);
- } else if (method_exists($this, "check_auth")) {
- // old (pre 1.0) method name
- return $this->check_auth(@$_SERVER["AUTH_TYPE"],
- @$_SERVER["PHP_AUTH_USER"],
- @$_SERVER["PHP_AUTH_PW"]);
- } else {
- // no method found -> no authentication required
- return true;
- }
- }
-
- // }}}
-
- // {{{ UUID stuff
-
- /**
- * generate Unique Universal IDentifier for lock token
- *
- * @param void
- * @return string a new UUID
- */
- function _new_uuid()
- {
- // use uuid extension from PECL if available
- if (function_exists("uuid_create")) {
- return uuid_create();
- }
-
- // fallback
- $uuid = md5(microtime().getmypid()); // this should be random enough for now
-
- // set variant and version fields for 'true' random uuid
- $uuid{12} = "4";
- $n = 8 + (ord($uuid{16}) & 3);
- $hex = "0123456789abcdef";
- $uuid{16} = $hex{$n};
-
- // return formated uuid
- return substr($uuid, 0, 8)."-"
- . substr($uuid, 8, 4)."-"
- . substr($uuid, 12, 4)."-"
- . substr($uuid, 16, 4)."-"
- . substr($uuid, 20);
- }
-
- /**
- * create a new opaque lock token as defined in RFC2518
- *
- * @param void
- * @return string new RFC2518 opaque lock token
- */
- function _new_locktoken()
- {
- return "opaquelocktoken:".$this->_new_uuid();
- }
-
- // }}}
-
- // {{{ WebDAV If: header parsing
-
- /**
- *
- *
- * @param string header string to parse
- * @param int current parsing position
- * @return array next token (type and value)
- */
- function _if_header_lexer($string, &$pos)
- {
- // skip whitespace
- while (ctype_space($string{$pos})) {
- ++$pos;
- }
-
- // already at end of string?
- if (strlen($string) <= $pos) {
- return false;
- }
-
- // get next character
- $c = $string{$pos++};
-
- // now it depends on what we found
- switch ($c) {
- case "<":
- // URIs are enclosed in <...>
- $pos2 = strpos($string, ">", $pos);
- $uri = substr($string, $pos, $pos2 - $pos);
- $pos = $pos2 + 1;
- return array("URI", $uri);
-
- case "[":
- //Etags are enclosed in [...]
- if ($string{$pos} == "W") {
- $type = "ETAG_WEAK";
- $pos += 2;
- } else {
- $type = "ETAG_STRONG";
- }
- $pos2 = strpos($string, "]", $pos);
- $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
- $pos = $pos2 + 1;
- return array($type, $etag);
-
- case "N":
- // "N" indicates negation
- $pos += 2;
- return array("NOT", "Not");
-
- default:
- // anything else is passed verbatim char by char
- return array("CHAR", $c);
- }
- }
-
- /**
- * parse If: header
- *
- * @param string header string
- * @return array URIs and their conditions
- */
- function _if_header_parser($str)
- {
- $pos = 0;
- $len = strlen($str);
-
- $uris = array();
-
- // parser loop
- while ($pos < $len) {
- // get next token
- $token = $this->_if_header_lexer($str, $pos);
-
- // check for URI
- if ($token[0] == "URI") {
- $uri = $token[1]; // remember URI
- $token = $this->_if_header_lexer($str, $pos); // get next token
- } else {
- $uri = "";
- }
-
- // sanity check
- if ($token[0] != "CHAR" || $token[1] != "(") {
- return false;
- }
-
- $list = array();
- $level = 1;
- $not = "";
- while ($level) {
- $token = $this->_if_header_lexer($str, $pos);
- if ($token[0] == "NOT") {
- $not = "!";
- continue;
- }
- switch ($token[0]) {
- case "CHAR":
- switch ($token[1]) {
- case "(":
- $level++;
- break;
- case ")":
- $level--;
- break;
- default:
- return false;
- }
- break;
-
- case "URI":
- $list[] = $not."<$token[1]>";
- break;
-
- case "ETAG_WEAK":
- $list[] = $not."[W/'$token[1]']>";
- break;
-
- case "ETAG_STRONG":
- $list[] = $not."['$token[1]']>";
- break;
-
- default:
- return false;
- }
- $not = "";
- }
-
- if (@is_array($uris[$uri])) {
- $uris[$uri] = array_merge($uris[$uri],$list);
- } else {
- $uris[$uri] = $list;
- }
- }
-
- return $uris;
- }
-
- /**
- * check if conditions from "If:" headers are meat
- *
- * the "If:" header is an extension to HTTP/1.1
- * defined in RFC 2518 section 9.4
- *
- * @param void
- * @return void
- */
- function _check_if_header_conditions()
- {
- if (isset($_SERVER["HTTP_IF"])) {
- $this->_if_header_uris =
- $this->_if_header_parser($_SERVER["HTTP_IF"]);
-
- foreach($this->_if_header_uris as $uri => $conditions) {
- if ($uri == "") {
- $uri = $this->uri;
- }
- // all must match
- $state = true;
- foreach($conditions as $condition) {
- // lock tokens may be free form (RFC2518 6.3)
- // but if opaquelocktokens are used (RFC2518 6.4)
- // we have to check the format (litmus tests this)
- if (!strncmp($condition, "$", $condition)) {
- return false;
- }
- }
- if (!$this->_check_uri_condition($uri, $condition)) {
- $state = false;
- break;
- }
- }
-
- // any match is ok
- if ($state == true) {
- return true;
- }
- }
- return false;
- }
- return true;
- }
-
- /**
- * Check a single URI condition parsed from an if-header
- *
- * Check a single URI condition parsed from an if-header
- *
- * @abstract
- * @param string $uri URI to check
- * @param string $condition Condition to check for this URI
- * @returns bool Condition check result
- */
- function _check_uri_condition($uri, $condition)
- {
- // not really implemented here,
- // implementations must override
- return true;
- }
-
-
- /**
- *
- *
- * @param string path of resource to check
- * @param bool exclusive lock?
- */
- function _check_lock_status($path, $exclusive_only = false)
- {
- // FIXME depth -> ignored for now
- if (method_exists($this, "checkLock")) {
- // is locked?
- $lock = $this->checkLock($path);
-
- // ... and lock is not owned?
- if (is_array($lock) && count($lock)) {
- // FIXME doesn't check uri restrictions yet
- if (!strstr($_SERVER["HTTP_IF"], $lock["token"])) {
- if (!$exclusive_only || ($lock["scope"] !== "shared"))
- return false;
- }
- }
- }
- return true;
- }
-
-
- // }}}
-
-
- /**
- * Generate lockdiscovery reply from checklock() result
- *
- * @param string resource path to check
- * @return string lockdiscovery response
- */
- function lockdiscovery($path)
- {
- // no lock support without checklock() method
- if (!method_exists($this, "checklock")) {
- return "";
- }
-
- // collect response here
- $activelocks = "";
-
- // get checklock() reply
- $lock = $this->checklock($path);
-
- // generate block for returned data
- if (is_array($lock) && count($lock)) {
- // check for 'timeout' or 'expires'
- if (!empty($lock["expires"])) {
- $timeout = "Second-".($lock["expires"] - time());
- } else if (!empty($lock["timeout"])) {
- $timeout = "Second-$lock[timeout]";
- } else {
- $timeout = "Infinite";
- }
-
- // genreate response block
- $activelocks.= "
-
-
-
- $lock[depth]
- $lock[owner]
- $timeout
- $lock[token]
-
- ";
- }
-
- // return generated response
- return $activelocks;
- }
-
- /**
- * set HTTP return status and mirror it in a private header
- *
- * @param string status code and message
- * @return void
- */
- function http_status($status)
- {
- // simplified success case
- if($status === true) {
- $status = "200 OK";
- }
-
- // remember status
- $this->_http_status = $status;
-
- // generate HTTP status response
- header("HTTP/1.1 $status");
- header("X-WebDAV-Status: $status", true);
- }
-
- /**
- * private minimalistic version of PHP urlencode()
- *
- * only blanks and XML special chars must be encoded here
- * full urlencode() encoding confuses some clients ...
- *
- * @param string URL to encode
- * @return string encoded URL
- */
- function _urlencode($url)
- {
- return strtr($url, array(" "=>"%20",
- "&"=>"%26",
- "<"=>"%3C",
- ">"=>"%3E",
- ));
- }
-
- /**
- * private version of PHP urldecode
- *
- * not really needed but added for completenes
- *
- * @param string URL to decode
- * @return string decoded URL
- */
- function _urldecode($path)
- {
- return urldecode($path);
- }
-
- /**
- * UTF-8 encode property values if not already done so
- *
- * @param string text to encode
- * @return string utf-8 encoded text
- */
- function _prop_encode($text)
- {
- switch (strtolower($this->_prop_encoding)) {
- case "utf-8":
- return $text;
- case "iso-8859-1":
- case "iso-8859-15":
- case "latin-1":
- default:
- return utf8_encode($text);
- }
- }
-
- /**
- * Slashify - make sure path ends in a slash
- *
- * @param string directory path
- * @returns string directory path wiht trailing slash
- */
- function _slashify($path) {
- if ($path[strlen($path)-1] != '/') {
- $path = $path."/";
- }
- return $path;
- }
-
- /**
- * Unslashify - make sure path doesn't in a slash
- *
- * @param string directory path
- * @returns string directory path wihtout trailing slash
- */
- function _unslashify($path) {
- if ($path[strlen($path)-1] == '/') {
- $path = substr($path, 0, strlen($path, 0, -1));
- }
- return $path;
- }
-
- /**
- * Merge two pathes, make sure there is exactly one slash between them
- *
- * @param string parent path
- * @param string child path
- * @return string merged path
- */
- function _mergePathes($parent, $child)
- {
- if ($child{0} == '/') {
- return $this->_unslashify($parent).$child;
- } else {
- return $this->_slashify($parent).$child;
- }
- }
-}
-
- /*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- */
-?>
diff --git a/thirdparty/pear/HTTP/WebDAV/Tools/_parse_lockinfo.php b/thirdparty/pear/HTTP/WebDAV/Tools/_parse_lockinfo.php
deleted file mode 100644
index 4a08100..0000000
--- a/thirdparty/pear/HTTP/WebDAV/Tools/_parse_lockinfo.php
+++ /dev/null
@@ -1,237 +0,0 @@
- |
-// | Christian Stocker |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-
-/**
- * helper class for parsing LOCK request bodies
- *
- * @package HTTP_WebDAV_Server
- * @author Hartmut Holzgraefe
- * @version 0.99.1dev
- */
-class _parse_lockinfo
-{
- /**
- * success state flag
- *
- * @var bool
- * @access public
- */
- var $success = false;
-
- /**
- * lock type, currently only "write"
- *
- * @var string
- * @access public
- */
- var $locktype = "";
-
- /**
- * lock scope, "shared" or "exclusive"
- *
- * @var string
- * @access public
- */
- var $lockscope = "";
-
- /**
- * lock owner information
- *
- * @var string
- * @access public
- */
- var $owner = "";
-
- /**
- * flag that is set during lock owner read
- *
- * @var bool
- * @access private
- */
- var $collect_owner = false;
-
- /**
- * constructor
- *
- * @param string path of stream to read
- * @access public
- */
- function _parse_lockinfo($path)
- {
- // we assume success unless problems occur
- $this->success = true;
-
- // remember if any input was parsed
- $had_input = false;
-
- // open stream
- $f_in = fopen($path, "r");
- if (!$f_in) {
- $this->success = false;
- return;
- }
-
- // create namespace aware parser
- $xml_parser = xml_parser_create_ns("UTF-8", " ");
-
- // set tag and data handlers
- xml_set_element_handler($xml_parser,
- array(&$this, "_startElement"),
- array(&$this, "_endElement"));
- xml_set_character_data_handler($xml_parser,
- array(&$this, "_data"));
-
- // we want a case sensitive parser
- xml_parser_set_option($xml_parser,
- XML_OPTION_CASE_FOLDING, false);
-
- // parse input
- while($this->success && !feof($f_in)) {
- $line = fgets($f_in);
- if (is_string($line)) {
- $had_input = true;
- $this->success &= xml_parse($xml_parser, $line, false);
- }
- }
-
- // finish parsing
- if($had_input) {
- $this->success &= xml_parse($xml_parser, "", true);
- }
-
- // check if required tags where found
- $this->success &= !empty($this->locktype);
- $this->success &= !empty($this->lockscope);
-
- // free parser resource
- xml_parser_free($xml_parser);
-
- // close input stream
- fclose($f_in);
- }
-
-
- /**
- * tag start handler
- *
- * @param resource parser
- * @param string tag name
- * @param array tag attributes
- * @return void
- * @access private
- */
- function _startElement($parser, $name, $attrs)
- {
- // namespace handling
- if (strstr($name, " ")) {
- list($ns, $tag) = explode(" ", $name);
- } else {
- $ns = "";
- $tag = $name;
- }
-
-
- if ($this->collect_owner) {
- // everything within the tag needs to be collected
- $ns_short = "";
- $ns_attr = "";
- if ($ns) {
- if ($ns == "DAV:") {
- $ns_short = "D:";
- } else {
- $ns_attr = " xmlns='$ns'";
- }
- }
- $this->owner .= "<$ns_short$tag$ns_attr>";
- } else if ($ns == "DAV:") {
- // parse only the essential tags
- switch ($tag) {
- case "write":
- $this->locktype = $tag;
- break;
- case "exclusive":
- case "shared":
- $this->lockscope = $tag;
- break;
- case "owner":
- $this->collect_owner = true;
- break;
- }
- }
- }
-
- /**
- * data handler
- *
- * @param resource parser
- * @param string data
- * @return void
- * @access private
- */
- function _data($parser, $data)
- {
- // only the tag has data content
- if ($this->collect_owner) {
- $this->owner .= $data;
- }
- }
-
- /**
- * tag end handler
- *
- * @param resource parser
- * @param string tag name
- * @return void
- * @access private
- */
- function _endElement($parser, $name)
- {
- // namespace handling
- if (strstr($name, " ")) {
- list($ns, $tag) = explode(" ", $name);
- } else {
- $ns = "";
- $tag = $name;
- }
-
- // finished?
- if (($ns == "DAV:") && ($tag == "owner")) {
- $this->collect_owner = false;
- }
-
- // within we have to collect everything
- if ($this->collect_owner) {
- $ns_short = "";
- $ns_attr = "";
- if ($ns) {
- if ($ns == "DAV:") {
- $ns_short = "D:";
- } else {
- $ns_attr = " xmlns='$ns'";
- }
- }
- $this->owner .= "$ns_short$tag$ns_attr>";
- }
- }
-}
-
-?>
\ No newline at end of file
diff --git a/thirdparty/pear/HTTP/WebDAV/Tools/_parse_propfind.php b/thirdparty/pear/HTTP/WebDAV/Tools/_parse_propfind.php
deleted file mode 100644
index ea16cd7..0000000
--- a/thirdparty/pear/HTTP/WebDAV/Tools/_parse_propfind.php
+++ /dev/null
@@ -1,178 +0,0 @@
- |
-// | Christian Stocker |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-
-/**
- * helper class for parsing PROPFIND request bodies
- *
- * @package HTTP_WebDAV_Server
- * @author Hartmut Holzgraefe
- * @version 0.99.1dev
- */
-class _parse_propfind
-{
- /**
- * success state flag
- *
- * @var bool
- * @access public
- */
- var $success = false;
-
- /**
- * found properties are collected here
- *
- * @var array
- * @access public
- */
- var $props = false;
-
- /**
- * internal tag nesting depth counter
- *
- * @var int
- * @access private
- */
- var $depth = 0;
-
-
- /**
- * constructor
- *
- * @access public
- */
- function _parse_propfind($path)
- {
- // success state flag
- $this->success = true;
-
- // property storage array
- $this->props = array();
-
- // internal tag depth counter
- $this->depth = 0;
-
- // remember if any input was parsed
- $had_input = false;
-
- // open input stream
- $f_in = fopen($path, "r");
- if (!$f_in) {
- $this->success = false;
- return;
- }
-
- // create XML parser
- $xml_parser = xml_parser_create_ns("UTF-8", " ");
-
- // set tag and data handlers
- xml_set_element_handler($xml_parser,
- array(&$this, "_startElement"),
- array(&$this, "_endElement"));
-
- // we want a case sensitive parser
- xml_parser_set_option($xml_parser,
- XML_OPTION_CASE_FOLDING, false);
-
-
- // parse input
- while($this->success && !feof($f_in)) {
- $line = fgets($f_in);
- if (is_string($line)) {
- $had_input = true;
- $this->success &= xml_parse($xml_parser, $line, false);
- }
- }
-
- // finish parsing
- if($had_input) {
- $this->success &= xml_parse($xml_parser, "", true);
- }
-
- // free parser
- xml_parser_free($xml_parser);
-
- // close input stream
- fclose($f_in);
-
- // if no input was parsed it was a request
- if(!count($this->props)) $this->props = "all"; // default
- }
-
-
- /**
- * start tag handler
- *
- * @access private
- * @param resource parser
- * @param string tag name
- * @param array tag attributes
- */
- function _startElement($parser, $name, $attrs)
- {
- // name space handling
- if (strstr($name, " ")) {
- list($ns, $tag) = explode(" ", $name);
- if ($ns == "")
- $this->success = false;
- } else {
- $ns = "";
- $tag = $name;
- }
-
- // special tags at level 1: and
- if ($this->depth == 1) {
- if ($tag == "allprop")
- $this->props = "all";
-
- if ($tag == "propname")
- $this->props = "names";
- }
-
- // requested properties are found at level 2
- if ($this->depth == 2) {
- $prop = array("name" => $tag);
- if ($ns)
- $prop["xmlns"] = $ns;
- $this->props[] = $prop;
- }
-
- // increment depth count
- $this->depth++;
- }
-
-
- /**
- * end tag handler
- *
- * @access private
- * @param resource parser
- * @param string tag name
- */
- function _endElement($parser, $name)
- {
- // here we only need to decrement the depth count
- $this->depth--;
- }
-}
-
-
-?>
\ No newline at end of file
diff --git a/thirdparty/pear/HTTP/WebDAV/Tools/_parse_proppatch.php b/thirdparty/pear/HTTP/WebDAV/Tools/_parse_proppatch.php
deleted file mode 100644
index 006efb5..0000000
--- a/thirdparty/pear/HTTP/WebDAV/Tools/_parse_proppatch.php
+++ /dev/null
@@ -1,214 +0,0 @@
- |
-// | Christian Stocker |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-
-/**
- * helper class for parsing PROPPATCH request bodies
- *
- * @package HTTP_WebDAV_Server
- * @author Hartmut Holzgraefe
- * @version 0.99.1dev
- */
-class _parse_proppatch
-{
- /**
- *
- *
- * @var
- * @access
- */
- var $success;
-
- /**
- *
- *
- * @var
- * @access
- */
- var $props;
-
- /**
- *
- *
- * @var
- * @access
- */
- var $depth;
-
- /**
- *
- *
- * @var
- * @access
- */
- var $mode;
-
- /**
- *
- *
- * @var
- * @access
- */
- var $current;
-
- /**
- * constructor
- *
- * @param string path of input stream
- * @access public
- */
- function _parse_proppatch($path)
- {
- $this->success = true;
-
- $this->depth = 0;
- $this->props = array();
- $had_input = false;
-
- $f_in = fopen($path, "r");
- if (!$f_in) {
- $this->success = false;
- return;
- }
-
- $xml_parser = xml_parser_create_ns("UTF-8", " ");
-
- xml_set_element_handler($xml_parser,
- array(&$this, "_startElement"),
- array(&$this, "_endElement"));
-
- xml_set_character_data_handler($xml_parser,
- array(&$this, "_data"));
-
- xml_parser_set_option($xml_parser,
- XML_OPTION_CASE_FOLDING, false);
-
- while($this->success && !feof($f_in)) {
- $line = fgets($f_in);
- if (is_string($line)) {
- $had_input = true;
- $this->success &= xml_parse($xml_parser, $line, false);
- }
- }
-
- if($had_input) {
- $this->success &= xml_parse($xml_parser, "", true);
- }
-
- xml_parser_free($xml_parser);
-
- fclose($f_in);
- }
-
- /**
- * tag start handler
- *
- * @param resource parser
- * @param string tag name
- * @param array tag attributes
- * @return void
- * @access private
- */
- function _startElement($parser, $name, $attrs)
- {
- if (strstr($name, " ")) {
- list($ns, $tag) = explode(" ", $name);
- if ($ns == "")
- $this->success = false;
- } else {
- $ns = "";
- $tag = $name;
- }
-
- if ($this->depth == 1) {
- $this->mode = $tag;
- }
-
- if ($this->depth == 3) {
- $prop = array("name" => $tag);
- $this->current = array("name" => $tag, "ns" => $ns, "status"=> 200);
- if ($this->mode == "set") {
- $this->current["val"] = ""; // default set val
- }
- }
-
- if ($this->depth >= 4) {
- $this->current["val"] .= "<$tag";
- foreach ($attr as $key => $val) {
- $this->current["val"] .= ' '.$key.'="'.str_replace('"','"', $val).'"';
- }
- $this->current["val"] .= ">";
- }
-
-
-
- $this->depth++;
- }
-
- /**
- * tag end handler
- *
- * @param resource parser
- * @param string tag name
- * @return void
- * @access private
- */
- function _endElement($parser, $name)
- {
- if (strstr($name, " ")) {
- list($ns, $tag) = explode(" ", $name);
- if ($ns == "")
- $this->success = false;
- } else {
- $ns = "";
- $tag = $name;
- }
-
- $this->depth--;
-
- if ($this->depth >= 4) {
- $this->current["val"] .= "$tag>";
- }
-
- if ($this->depth == 3) {
- if (isset($this->current)) {
- $this->props[] = $this->current;
- unset($this->current);
- }
- }
- }
-
- /**
- * input data handler
- *
- * @param resource parser
- * @param string data
- * @return void
- * @access private
- */
- function _data($parser, $data) {
- if (isset($this->current)) {
- $this->current["val"] .= $data;
- }
- }
-}
-
-?>
\ No newline at end of file
diff --git a/thirdparty/pear/WebDAV/Client.php b/thirdparty/pear/WebDAV/Client.php
new file mode 100644
index 0000000..2388be9
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Client.php
@@ -0,0 +1,461 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Client.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+
+require_once 'HTTP/Request.php';
+require_once 'HTTP/Client/CookieManager.php';
+
+/**
+ * A simple HTTP client class.
+ *
+ * The class wraps around HTTP_Request providing a higher-level
+ * API for performing multiple HTTP requests
+ *
+ * @package HTTP_Client
+ * @author Alexey Borzov
+ * @version $Revision: 6819 $
+ */
+class HTTP_Client
+{
+ /**
+ * An HTTP_Client_CookieManager instance
+ * @var object
+ */
+ var $_cookieManager;
+
+ /**
+ * Received HTTP responses
+ * @var array
+ */
+ var $_responses;
+
+ /**
+ * Default headers to send on every request
+ * @var array
+ */
+ var $_defaultHeaders = array();
+
+ /**
+ * Default parameters for HTTP_Request's constructor
+ * @var array
+ */
+ var $_defaultRequestParams = array();
+
+ /**
+ * How many redirects were done
+ * @var integer
+ */
+ var $_redirectCount = 0;
+
+ /**
+ * Maximum allowed redirects
+ * @var integer
+ */
+ var $_maxRedirects = 5;
+
+ /**
+ * Listeners attached to the client
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Whether the listener should be propagated to Request objects
+ * @var array
+ */
+ var $_propagate = array();
+
+ /**
+ * Whether to keep all the responses or just the most recent one
+ * @var boolean
+ */
+ var $_isHistoryEnabled = true;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param array Parameters to pass to HTTP_Request's constructor
+ * @param array Default headers to send on every request
+ */
+ function HTTP_Client($defaultRequestParams = null, $defaultHeaders = null)
+ {
+ $this->_cookieManager =& new HTTP_Client_CookieManager();
+ if (isset($defaultHeaders)) {
+ $this->setDefaultHeader($defaultHeaders);
+ }
+ if (isset($defaultRequestParams)) {
+ $this->setRequestParameter($defaultRequestParams);
+ }
+ }
+
+
+ /**
+ * Sets the maximum redirects that will be processed.
+ *
+ * Setting this to 0 disables redirect processing. If not 0 and the
+ * number of redirects in a request is bigger than this number, then an
+ * error will be raised.
+ *
+ * @access public
+ * @param int Max number of redirects to process
+ */
+ function setMaxRedirects($value)
+ {
+ $this->_maxRedirects = $value;
+ }
+
+
+ /**
+ * Sets whether to keep all the responses or just the most recent one
+ *
+ * @access public
+ * @param bool Whether to enable history
+ */
+ function enableHistory($enable)
+ {
+ $this->_isHistoryEnabled = (bool)$enable;
+ }
+
+ /**
+ * Creates a HTTP_Request objects, applying all the necessary defaults
+ *
+ * @param string URL
+ * @param integer Method, constants are defined in HTTP_Request
+ * @access private
+ * @return object HTTP_Request object with all defaults applied
+ */
+ function &_createRequest($url, $method = HTTP_REQUEST_METHOD_GET)
+ {
+ $req =& new HTTP_Request($url, $this->_defaultRequestParams);
+ $req->setMethod($method);
+ foreach ($this->_defaultHeaders as $name => $value) {
+ $req->addHeader($name, $value);
+ }
+ $this->_cookieManager->passCookies($req);
+ foreach ($this->_propagate as $id => $propagate) {
+ if ($propagate) {
+ $req->attach($this->_listeners[$id]);
+ }
+ }
+ return $req;
+ }
+
+
+ /**
+ * Sends a 'HEAD' HTTP request
+ *
+ * @param string URL
+ * @access public
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function head($url)
+ {
+ $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_HEAD);
+ return $this->_performRequest($request);
+ }
+
+
+ /**
+ * Sends a 'GET' HTTP request
+ *
+ * @param string URL
+ * @param mixed additional data to send
+ * @param boolean Whether the data is already urlencoded
+ * @access public
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function get($url, $data = null, $preEncoded = false)
+ {
+ $request =& $this->_createRequest($url);
+ if (is_array($data)) {
+ foreach ($data as $name => $value) {
+ $request->addQueryString($name, $value, $preEncoded);
+ }
+ } elseif (isset($data)) {
+ $request->addRawQueryString($data, $preEncoded);
+ }
+ return $this->_performRequest($request);
+ }
+
+
+ /**
+ * Sends a 'POST' HTTP request
+ *
+ * @param string URL
+ * @param mixed Data to send
+ * @param boolean Whether the data is already urlencoded
+ * @param array Files to upload. Elements of the array should have the form:
+ * array(name, filename(s)[, content type]), see HTTP_Request::addFile()
+ * @access public
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function post($url, $data, $preEncoded = false, $files = array())
+ {
+ $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_POST);
+ if (is_array($data)) {
+ foreach ($data as $name => $value) {
+ $request->addPostData($name, $value, $preEncoded);
+ }
+ } else {
+ $request->addRawPostData($data, $preEncoded);
+ }
+ foreach ($files as $fileData) {
+ $res = call_user_func_array(array(&$request, 'addFile'), $fileData);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ return $this->_performRequest($request);
+ }
+
+
+ /**
+ * Sets default header(s) for HTTP requests
+ *
+ * @param mixed header name or array ('header name' => 'header value')
+ * @param string header value if $name is not an array
+ * @access public
+ */
+ function setDefaultHeader($name, $value = null)
+ {
+ if (is_array($name)) {
+ $this->_defaultHeaders = array_merge($this->_defaultHeaders, $name);
+ } else {
+ $this->_defaultHeaders[$name] = $value;
+ }
+ }
+
+
+ /**
+ * Sets parameter(s) for HTTP requests
+ *
+ * @param mixed parameter name or array ('parameter name' => 'parameter value')
+ * @param string parameter value if $name is not an array
+ * @access public
+ */
+ function setRequestParameter($name, $value = null)
+ {
+ if (is_array($name)) {
+ $this->_defaultRequestParams = array_merge($this->_defaultRequestParams, $name);
+ } else {
+ $this->_defaultRequestParams[$name] = $value;
+ }
+ }
+
+
+ /**
+ * Performs a request, processes redirects
+ *
+ * @param object HTTP_Request object
+ * @access private
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function _performRequest(&$request)
+ {
+ // If this is not a redirect, notify the listeners of new request
+ if (0 == $this->_redirectCount) {
+ $this->_notify('request', $request->_url->getUrl());
+ }
+ if (PEAR::isError($err = $request->sendRequest())) {
+ return $err;
+ }
+ $this->_pushResponse($request);
+
+ $code = $request->getResponseCode();
+ if ($this->_maxRedirects > 0 && in_array($code, array(300, 301, 302, 303, 307))) {
+ if (++$this->_redirectCount > $this->_maxRedirects) {
+ return PEAR::raiseError('Too many redirects');
+ }
+ $location = $request->getResponseHeader('Location');
+ if ('' == $location) {
+ return PEAR::raiseError("No 'Location' field on redirect");
+ }
+ $url = $this->_redirectUrl($request->_url, $location);
+ // Notify of redirection
+ $this->_notify('httpRedirect', $url);
+ // we access the private properties directly, as there are no accessors for them
+ switch ($request->_method) {
+ case HTTP_REQUEST_METHOD_POST:
+ if (302 == $code || 303 == $code) {
+ return $this->get($url);
+ } else {
+ $postFiles = array();
+ foreach ($request->_postFiles as $name => $data) {
+ $postFiles[] = array($name, $data['name'], $data['type']);
+ }
+ return $this->post($url, $request->_postData, true, $postFiles);
+ }
+ case HTTP_REQUEST_METHOD_HEAD:
+ return (303 == $code? $this->get($url): $this->head($url));
+ case HTTP_REQUEST_METHOD_GET:
+ default:
+ return $this->get($url);
+ } // switch
+
+ } else {
+ $this->_redirectCount = 0;
+ if (400 >= $code) {
+ $this->_notify('httpSuccess');
+ $this->setDefaultHeader('Referer', $request->_url->getUrl());
+ // some result processing should go here
+ } else {
+ $this->_notify('httpError');
+ }
+ }
+ return $code;
+ }
+
+
+ /**
+ * Returns the most recent HTTP response
+ *
+ * @access public
+ * @return array
+ */
+ function ¤tResponse()
+ {
+ return $this->_responses[count($this->_responses) - 1];
+ }
+
+
+ /**
+ * Saves the server's response to responses list
+ *
+ * @param object HTTP_Request object, with request already sent
+ * @access private
+ */
+ function _pushResponse(&$request)
+ {
+ $this->_cookieManager->updateCookies($request);
+ $idx = $this->_isHistoryEnabled? count($this->_responses): 0;
+ $this->_responses[$idx] = array(
+ 'code' => $request->getResponseCode(),
+ 'headers' => $request->getResponseHeader(),
+ 'body' => $request->getResponseBody()
+ );
+ }
+
+
+ /**
+ * Clears object's internal properties
+ *
+ * @access public
+ */
+ function reset()
+ {
+ $this->_cookieManager->reset();
+ $this->_responses = array();
+ $this->_defaultHeaders = array();
+ $this->_defaultRequestParams = array();
+ }
+
+
+ /**
+ * Adds a Listener to the list of listeners that are notified of
+ * the object's events
+ *
+ * @param object HTTP_Request_Listener instance to attach
+ * @param boolean Whether the listener should be attached to the
+ * created HTTP_Request objects
+ * @return boolean whether the listener was successfully attached
+ * @access public
+ */
+ function attach(&$listener, $propagate = false)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
+ return false;
+ }
+ $this->_listeners[$listener->getId()] =& $listener;
+ $this->_propagate[$listener->getId()] = $propagate;
+ return true;
+ }
+
+
+ /**
+ * Removes a Listener from the list of listeners
+ *
+ * @param object HTTP_Request_Listener instance to detach
+ * @return boolean whether the listener was successfully detached
+ * @access public
+ */
+ function detach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
+ !isset($this->_listeners[$listener->getId()])) {
+ return false;
+ }
+ unset($this->_listeners[$listener->getId()], $this->_propagate[$listener->getId()]);
+ return true;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * Currently available events are:
+ * 'request': sent on HTTP request that is not a redirect
+ * 'httpSuccess': sent when we receive a successfull 2xx response
+ * 'httpRedirect': sent when we receive a redirection response
+ * 'httpError': sent on 4xx, 5xx response
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+
+
+ /**
+ * Calculates the absolute URL of a redirect
+ *
+ * @param object Net_Url object containing the request URL
+ * @param string Value of the 'Location' response header
+ * @return string Absolute URL we are being redirected to
+ * @access private
+ */
+ function _redirectUrl($url, $location)
+ {
+ if (preg_match('!^https?://!i', $location)) {
+ return $location;
+ } else {
+ if ('/' == $location{0}) {
+ $url->path = Net_URL::resolvePath($location);
+ } elseif('/' == substr($url->path, -1)) {
+ $url->path = Net_URL::resolvePath($url->path . $location);
+ } else {
+ $dirname = (DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path));
+ $url->path = Net_URL::resolvePath($dirname . '/' . $location);
+ }
+ $url->querystring = array();
+ $url->anchor = '';
+ return $url->getUrl();
+ }
+ }
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Client/CookieManager.php b/thirdparty/pear/WebDAV/Client/CookieManager.php
new file mode 100644
index 0000000..b5518e8
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Client/CookieManager.php
@@ -0,0 +1,183 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: CookieManager.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+
+/**
+ * This class is used to store cookies and pass them between HTTP requests.
+ *
+ * @package HTTP_Client
+ * @author Alexey Borzov
+ * @version $Revision: 6819 $
+ */
+class HTTP_Client_CookieManager
+{
+ /**
+ * An array containing cookie values
+ * @var array
+ */
+ var $_cookies = array();
+
+
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ function HTTP_Client_CookieManager()
+ {
+ // abstract
+ }
+
+
+ /**
+ * Adds cookies to the request
+ *
+ * @access public
+ * @param object An HTTP_Request object
+ */
+ function passCookies(&$request)
+ {
+ if (!empty($this->_cookies)) {
+ $url =& $request->_url;
+ // We do not check cookie's "expires" field, as we do not store deleted
+ // cookies in the array and our client does not work long enough for other
+ // cookies to expire. If some kind of persistence is added to this object,
+ // then expiration should be checked upon loading and session cookies should
+ // be cleared on saving.
+ $cookies = array();
+ foreach ($this->_cookies as $cookie) {
+ if ($this->_domainMatch($url->host, $cookie['domain']) && (0 === strpos($url->path, $cookie['path']))
+ && (empty($cookie['secure']) || $url->protocol == 'https')) {
+ $cookies[$cookie['name']][strlen($cookie['path'])] = $cookie['value'];
+ }
+ }
+ // cookies with longer paths go first
+ foreach ($cookies as $name => $values) {
+ krsort($values);
+ foreach ($values as $value) {
+ $request->addCookie($name, $value);
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Explicitly adds cookie to the list
+ *
+ * @param array An array representing cookie, this function expects all of the array's
+ * fields to be set
+ * @access public
+ */
+ function addCookie($cookie)
+ {
+ $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
+ $this->_cookies[$hash] = $cookie;
+ }
+
+
+ /**
+ * Updates cookie list from HTTP server response
+ *
+ * @access public
+ * @param object An HTTP_Request object with sendRequest() already done
+ */
+ function updateCookies(&$request)
+ {
+ if (false !== ($cookies = $request->getResponseCookies())) {
+ $url =& $request->_url;
+ foreach ($cookies as $cookie) {
+ // use the current domain by default
+ if (!isset($cookie['domain'])) {
+ $cookie['domain'] = $url->host;
+ }
+ // use the path to the current page by default
+ if (!isset($cookie['path'])) {
+ $cookie['path'] = DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path);
+ }
+ // check if the domains match
+ if ($this->_domainMatch($url->host, $cookie['domain'])) {
+ $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
+ // if value is empty or the time is in the past the cookie is deleted, else added
+ if (strlen($cookie['value'])
+ && (!isset($cookie['expires']) || (strtotime($cookie['expires']) > time()))) {
+ $this->_cookies[$hash] = $cookie;
+ } elseif (isset($this->_cookies[$hash])) {
+ unset($this->_cookies[$hash]);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Generates a key for the $_cookies array.
+ *
+ * The cookies is uniquely identified by its name, domain and path.
+ * Thus we cannot make f.e. an associative array with name as a key, we should
+ * generate a key from these 3 values.
+ *
+ * @access private
+ * @param string Cookie name
+ * @param string Cookie domain
+ * @param string Cookie path
+ * @return string a key
+ */
+ function _makeHash($name, $domain, $path)
+ {
+ return md5($name . "\r\n" . $domain . "\r\n" . $path);
+ }
+
+
+ /**
+ * Checks whether a cookie domain matches a request host.
+ *
+ * Cookie domain can begin with a dot, it also must contain at least
+ * two dots.
+ *
+ * @access private
+ * @param string request host
+ * @param string cookie domain
+ * @return bool match success
+ */
+ function _domainMatch($requestHost, $cookieDomain)
+ {
+ if ('.' != $cookieDomain{0}) {
+ return $requestHost == $cookieDomain;
+ } elseif (substr_count($cookieDomain, '.') < 2) {
+ return false;
+ } else {
+ return substr('.'. $requestHost, - strlen($cookieDomain)) == $cookieDomain;
+ }
+ }
+
+
+ /**
+ * Clears the $_cookies array
+ *
+ * @access public
+ */
+ function reset()
+ {
+ $this->_cookies = array();
+ }
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Download.php b/thirdparty/pear/WebDAV/Download.php
new file mode 100644
index 0000000..65a96f3
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Download.php
@@ -0,0 +1,1031 @@
+
+ * @copyright 2003-2005 Michael Wallner
+ * @license BSD, revised
+ * @version CVS: $Id: Download.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+ * @link http://pear.php.net/package/HTTP_Download
+ */
+
+// {{{ includes
+/**
+ * Requires PEAR
+ */
+require_once 'PEAR.php';
+
+/**
+ * Requires HTTP_Header
+ */
+require_once 'HTTP/Header.php';
+// }}}
+
+// {{{ constants
+/**#@+ Use with HTTP_Download::setContentDisposition() **/
+/**
+ * Send data as attachment
+ */
+define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment');
+/**
+ * Send data inline
+ */
+define('HTTP_DOWNLOAD_INLINE', 'inline');
+/**#@-**/
+
+/**#@+ Use with HTTP_Download::sendArchive() **/
+/**
+ * Send as uncompressed tar archive
+ */
+define('HTTP_DOWNLOAD_TAR', 'TAR');
+/**
+ * Send as gzipped tar archive
+ */
+define('HTTP_DOWNLOAD_TGZ', 'TGZ');
+/**
+ * Send as bzip2 compressed tar archive
+ */
+define('HTTP_DOWNLOAD_BZ2', 'BZ2');
+/**
+ * Send as zip archive
+ */
+define('HTTP_DOWNLOAD_ZIP', 'ZIP');
+/**#@-**/
+
+/**#@+
+ * Error constants
+ */
+define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1);
+define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2);
+define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3);
+define('HTTP_DOWNLOAD_E_INVALID_FILE', -4);
+define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5);
+define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6);
+define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7);
+define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8);
+define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9);
+/**#@-**/
+// }}}
+
+/**
+ * Send HTTP Downloads/Responses.
+ *
+ * With this package you can handle (hidden) downloads.
+ * It supports partial downloads, resuming and sending
+ * raw data ie. from database BLOBs.
+ *
+ * ATTENTION:
+ * You shouldn't use this package together with ob_gzhandler or
+ * zlib.output_compression enabled in your php.ini, especially
+ * if you want to send already gzipped data!
+ *
+ * @access public
+ * @version $Revision: 6819 $
+ */
+class HTTP_Download
+{
+ // {{{ protected member variables
+ /**
+ * Path to file for download
+ *
+ * @see HTTP_Download::setFile()
+ * @access protected
+ * @var string
+ */
+ var $file = '';
+
+ /**
+ * Data for download
+ *
+ * @see HTTP_Download::setData()
+ * @access protected
+ * @var string
+ */
+ var $data = null;
+
+ /**
+ * Resource handle for download
+ *
+ * @see HTTP_Download::setResource()
+ * @access protected
+ * @var int
+ */
+ var $handle = null;
+
+ /**
+ * Whether to gzip the download
+ *
+ * @access protected
+ * @var bool
+ */
+ var $gzip = false;
+
+ /**
+ * Whether to allow caching of the download on the clients side
+ *
+ * @access protected
+ * @var bool
+ */
+ var $cache = true;
+
+ /**
+ * Size of download
+ *
+ * @access protected
+ * @var int
+ */
+ var $size = 0;
+
+ /**
+ * Last modified
+ *
+ * @access protected
+ * @var int
+ */
+ var $lastModified = 0;
+
+ /**
+ * HTTP headers
+ *
+ * @access protected
+ * @var array
+ */
+ var $headers = array(
+ 'Content-Type' => 'application/x-octetstream',
+ 'Pragma' => 'cache',
+ 'Cache-Control' => 'public, must-revalidate, max-age=0',
+ 'Accept-Ranges' => 'bytes',
+ 'X-Sent-By' => 'PEAR::HTTP::Download'
+ );
+
+ /**
+ * HTTP_Header
+ *
+ * @access protected
+ * @var object
+ */
+ var $HTTP = null;
+
+ /**
+ * ETag
+ *
+ * @access protected
+ * @var string
+ */
+ var $etag = '';
+
+ /**
+ * Buffer Size
+ *
+ * @access protected
+ * @var int
+ */
+ var $bufferSize = 2097152;
+
+ /**
+ * Throttle Delay
+ *
+ * @access protected
+ * @var float
+ */
+ var $throttleDelay = 0;
+
+ /**
+ * Sent Bytes
+ *
+ * @access public
+ * @var int
+ */
+ var $sentBytes = 0;
+ // }}}
+
+ // {{{ constructor
+ /**
+ * Constructor
+ *
+ * Set supplied parameters.
+ *
+ * @access public
+ * @param array $params associative array of parameters
+ *
+ * one of:
+ * o 'file' => path to file for download
+ * o 'data' => raw data for download
+ * o 'resource' => resource handle for download
+ *
+ * and any of:
+ * o 'cache' => whether to allow cs caching
+ * o 'gzip' => whether to gzip the download
+ * o 'lastmodified' => unix timestamp
+ * o 'contenttype' => content type of download
+ * o 'contentdisposition' => content disposition
+ * o 'buffersize' => amount of bytes to buffer
+ * o 'throttledelay' => amount of secs to sleep
+ * o 'cachecontrol' => cache privacy and validity
+ *
+ *
+ * 'Content-Disposition' is not HTTP compliant, but most browsers
+ * follow this header, so it was borrowed from MIME standard.
+ *
+ * It looks like this:
+ * "Content-Disposition: attachment; filename=example.tgz".
+ *
+ * @see HTTP_Download::setContentDisposition()
+ */
+ function HTTP_Download($params = array())
+ {
+ $this->HTTP = &new HTTP_Header;
+ $this->setParams($params);
+ }
+ // }}}
+
+ // {{{ public methods
+ /**
+ * Set parameters
+ *
+ * Set supplied parameters through its accessor methods.
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param array $params associative array of parameters
+ *
+ * @see HTTP_Download::HTTP_Download()
+ */
+ function setParams($params)
+ {
+ foreach((array) $params as $param => $value){
+ $method = 'set'. $param;
+
+ if (!method_exists($this, $method)) {
+ return PEAR::raiseError(
+ "Method '$method' doesn't exist.",
+ HTTP_DOWNLOAD_E_INVALID_PARAM
+ );
+ }
+
+ $e = call_user_func_array(array(&$this, $method), (array) $value);
+
+ if (PEAR::isError($e)) {
+ return $e;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Set path to file for download
+ *
+ * The Last-Modified header will be set to files filemtime(), actually.
+ * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist.
+ * Sends HTTP 404 status if $send_404 is set to true.
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file path to file for download
+ * @param bool $send_404 whether to send HTTP/404 if
+ * the file wasn't found
+ */
+ function setFile($file, $send_404 = true)
+ {
+ $file = realpath($file);
+ if (!is_file($file)) {
+ if ($send_404) {
+ $this->HTTP->sendStatusCode(404);
+ }
+ return PEAR::raiseError(
+ "File '$file' not found.",
+ HTTP_DOWNLOAD_E_INVALID_FILE
+ );
+ }
+ $this->setLastModified(filemtime($file));
+ $this->file = $file;
+ $this->size = filesize($file);
+ return true;
+ }
+
+ /**
+ * Set data for download
+ *
+ * Set $data to null if you want to unset this.
+ *
+ * @access public
+ * @return void
+ * @param $data raw data to send
+ */
+ function setData($data = null)
+ {
+ $this->data = $data;
+ $this->size = strlen($data);
+ }
+
+ /**
+ * Set resource for download
+ *
+ * The resource handle supplied will be closed after sending the download.
+ * Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle
+ * is no valid resource. Set $handle to null if you want to unset this.
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param int $handle resource handle
+ */
+ function setResource($handle = null)
+ {
+ if (!isset($handle)) {
+ $this->handle = null;
+ $this->size = 0;
+ return true;
+ }
+
+ if (is_resource($handle)) {
+ $this->handle = $handle;
+ $filestats = fstat($handle);
+ $this->size = $filestats['size'];
+ return true;
+ }
+
+ return PEAR::raiseError(
+ "Handle '$handle' is no valid resource.",
+ HTTP_DOWNLOAD_E_INVALID_RESOURCE
+ );
+ }
+
+ /**
+ * Whether to gzip the download
+ *
+ * Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB)
+ * if ext/zlib is not available/loadable.
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param bool $gzip whether to gzip the download
+ */
+ function setGzip($gzip = false)
+ {
+ if ($gzip && !PEAR::loadExtension('zlib')){
+ return PEAR::raiseError(
+ 'GZIP compression (ext/zlib) not available.',
+ HTTP_DOWNLOAD_E_NO_EXT_ZLIB
+ );
+ }
+ $this->gzip = (bool) $gzip;
+ return true;
+ }
+
+ /**
+ * Whether to allow caching
+ *
+ * If set to true (default) we'll send some headers that are commonly
+ * used for caching purposes like ETag, Cache-Control and Last-Modified.
+ *
+ * If caching is disabled, we'll send the download no matter if it
+ * would actually be cached at the client side.
+ *
+ * @access public
+ * @return void
+ * @param bool $cache whether to allow caching
+ */
+ function setCache($cache = true)
+ {
+ $this->cache = (bool) $cache;
+ }
+
+ /**
+ * Whether to allow proxies to cache
+ *
+ * If set to 'private' proxies shouldn't cache the response.
+ * This setting defaults to 'public' and affects only cached responses.
+ *
+ * @access public
+ * @return bool
+ * @param string $cache private or public
+ * @param int $maxage maximum age of the client cache entry
+ */
+ function setCacheControl($cache = 'public', $maxage = 0)
+ {
+ switch ($cache = strToLower($cache))
+ {
+ case 'private':
+ case 'public':
+ $this->headers['Cache-Control'] =
+ $cache .', must-revalidate, max-age='. abs($maxage);
+ return true;
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Set ETag
+ *
+ * Sets a user-defined ETag for cache-validation. The ETag is usually
+ * generated by HTTP_Download through its payload information.
+ *
+ * @access public
+ * @return void
+ * @param string $etag Entity tag used for strong cache validation.
+ */
+ function setETag($etag = null)
+ {
+ $this->etag = (string) $etag;
+ }
+
+ /**
+ * Set Size of Buffer
+ *
+ * The amount of bytes specified as buffer size is the maximum amount
+ * of data read at once from resources or files. The default size is 2M
+ * (2097152 bytes). Be aware that if you enable gzip compression and
+ * you set a very low buffer size that the actual file size may grow
+ * due to added gzip headers for each sent chunk of the specified size.
+ *
+ * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not
+ * greater than 0 bytes.
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param int $bytes Amount of bytes to use as buffer.
+ */
+ function setBufferSize($bytes = 2097152)
+ {
+ if (0 >= $bytes) {
+ return PEAR::raiseError(
+ 'Buffer size must be greater than 0 bytes ('. $bytes .' given)',
+ HTTP_DOWNLOAD_E_INVALID_PARAM);
+ }
+ $this->bufferSize = abs($bytes);
+ return true;
+ }
+
+ /**
+ * Set Throttle Delay
+ *
+ * Set the amount of seconds to sleep after each chunck that has been
+ * sent. One can implement some sort of throttle through adjusting the
+ * buffer size and the throttle delay. With the following settings
+ * HTTP_Download will sleep a second after each 25 K of data sent.
+ *
+ *
+ * Array(
+ * 'throttledelay' => 1,
+ * 'buffersize' => 1024 * 25,
+ * )
+ *
+ *
+ * Just be aware that if gzipp'ing is enabled, decreasing the chunk size
+ * too much leads to proportionally increased network traffic due to added
+ * gzip header and bottom bytes around each chunk.
+ *
+ * @access public
+ * @return void
+ * @param float $seconds Amount of seconds to sleep after each
+ * chunk that has been sent.
+ */
+ function setThrottleDelay($seconds = 0)
+ {
+ $this->throttleDelay = abs($seconds) * 1000;
+ }
+
+ /**
+ * Set "Last-Modified"
+ *
+ * This is usually determined by filemtime() in HTTP_Download::setFile()
+ * If you set raw data for download with HTTP_Download::setData() and you
+ * want do send an appropiate "Last-Modified" header, you should call this
+ * method.
+ *
+ * @access public
+ * @return void
+ * @param int unix timestamp
+ */
+ function setLastModified($last_modified)
+ {
+ $this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified;
+ }
+
+ /**
+ * Set Content-Disposition header
+ *
+ * @see HTTP_Download::HTTP_Download
+ *
+ * @access public
+ * @return void
+ * @param string $disposition whether to send the download
+ * inline or as attachment
+ * @param string $file_name the filename to display in
+ * the browser's download window
+ *
+ * Example:
+ *
+ * $HTTP_Download->setContentDisposition(
+ * HTTP_DOWNLOAD_ATTACHMENT,
+ * 'download.tgz'
+ * );
+ *
+ */
+ function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT,
+ $file_name = null)
+ {
+ $cd = $disposition;
+ if (isset($file_name)) {
+ $cd .= '; filename="' . $file_name . '"';
+ } elseif ($this->file) {
+ $cd .= '; filename="' . basename($this->file) . '"';
+ }
+ $this->headers['Content-Disposition'] = $cd;
+ }
+
+ /**
+ * Set content type of the download
+ *
+ * Default content type of the download will be 'application/x-octetstream'.
+ * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if
+ * $content_type doesn't seem to be valid.
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $content_type content type of file for download
+ */
+ function setContentType($content_type = 'application/x-octetstream')
+ {
+ if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) {
+ return PEAR::raiseError(
+ "Invalid content type '$content_type' supplied.",
+ HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
+ );
+ }
+ $this->headers['Content-Type'] = $content_type;
+ return true;
+ }
+
+ /**
+ * Guess content type of file
+ *
+ * First we try to use PEAR::MIME_Type, if installed, to detect the content
+ * type, else we check if ext/mime_magic is loaded and properly configured.
+ *
+ * Returns PEAR_Error if:
+ * o if PEAR::MIME_Type failed to detect a proper content type
+ * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
+ * o ext/magic.mime is not installed, or not properly configured
+ * (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC)
+ * o mime_content_type() couldn't guess content type or returned
+ * a content type considered to be bogus by setContentType()
+ * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ */
+ function guessContentType()
+ {
+ if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') {
+ if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) {
+ return PEAR::raiseError($mime_type->getMessage(),
+ HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE);
+ }
+ return $this->setContentType($mime_type);
+ }
+ if (!function_exists('mime_content_type')) {
+ return PEAR::raiseError(
+ 'This feature requires ext/mime_magic!',
+ HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
+ );
+ }
+ if (!is_file(ini_get('mime_magic.magicfile'))) {
+ return PEAR::raiseError(
+ 'ext/mime_magic is loaded but not properly configured!',
+ HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
+ );
+ }
+ if (!$content_type = @mime_content_type($this->file)) {
+ return PEAR::raiseError(
+ 'Couldn\'t guess content type with mime_content_type().',
+ HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
+ );
+ }
+ return $this->setContentType($content_type);
+ }
+
+ /**
+ * Send
+ *
+ * Returns PEAR_Error if:
+ * o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT)
+ * o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST)
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param bool $autoSetContentDisposition Whether to set the
+ * Content-Disposition header if it isn't already.
+ */
+ function send($autoSetContentDisposition = true)
+ {
+ if (headers_sent()) {
+ return PEAR::raiseError(
+ 'Headers already sent.',
+ HTTP_DOWNLOAD_E_HEADERS_SENT
+ );
+ }
+
+ if (!ini_get('safe_mode')) {
+ @set_time_limit(0);
+ }
+
+ if ($autoSetContentDisposition &&
+ !isset($this->headers['Content-Disposition'])) {
+ $this->setContentDisposition();
+ }
+
+ if ($this->cache) {
+ $this->headers['ETag'] = $this->generateETag();
+ if ($this->isCached()) {
+ $this->HTTP->sendStatusCode(304);
+ $this->sendHeaders();
+ return true;
+ }
+ } else {
+ unset($this->headers['Last-Modified']);
+ }
+
+ while (@ob_end_clean());
+
+ if ($this->gzip) {
+ @ob_start('ob_gzhandler');
+ } else {
+ ob_start();
+ }
+
+ $this->sentBytes = 0;
+
+ if ($this->isRangeRequest()) {
+ $this->HTTP->sendStatusCode(206);
+ $chunks = $this->getChunks();
+ } else {
+ $this->HTTP->sendStatusCode(200);
+ $chunks = array(array(0, $this->size));
+ if (!$this->gzip && count(ob_list_handlers()) < 2) {
+ $this->headers['Content-Length'] = $this->size;
+ }
+ }
+
+ if (PEAR::isError($e = $this->sendChunks($chunks))) {
+ ob_end_clean();
+ $this->HTTP->sendStatusCode(416);
+ return $e;
+ }
+
+ ob_end_flush();
+ flush();
+ return true;
+ }
+
+ /**
+ * Static send
+ *
+ * @see HTTP_Download::HTTP_Download()
+ * @see HTTP_Download::send()
+ *
+ * @static
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param array $params associative array of parameters
+ * @param bool $guess whether HTTP_Download::guessContentType()
+ * should be called
+ */
+ function staticSend($params, $guess = false)
+ {
+ $d = &new HTTP_Download();
+ $e = $d->setParams($params);
+ if (PEAR::isError($e)) {
+ return $e;
+ }
+ if ($guess) {
+ $e = $d->guessContentType();
+ if (PEAR::isError($e)) {
+ return $e;
+ }
+ }
+ return $d->send();
+ }
+
+ /**
+ * Send a bunch of files or directories as an archive
+ *
+ * Example:
+ *
+ * require_once 'HTTP/Download.php';
+ * HTTP_Download::sendArchive(
+ * 'myArchive.tgz',
+ * '/var/ftp/pub/mike',
+ * HTTP_DOWNLOAD_TGZ,
+ * '',
+ * '/var/ftp/pub'
+ * );
+ *
+ *
+ * @see Archive_Tar::createModify()
+ * @deprecated use HTTP_Download_Archive::send()
+ * @static
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $name name the sent archive should have
+ * @param mixed $files files/directories
+ * @param string $type archive type
+ * @param string $add_path path that should be prepended to the files
+ * @param string $strip_path path that should be stripped from the files
+ */
+ function sendArchive( $name,
+ $files,
+ $type = HTTP_DOWNLOAD_TGZ,
+ $add_path = '',
+ $strip_path = '')
+ {
+ require_once 'HTTP/Download/Archive.php';
+ return HTTP_Download_Archive::send($name, $files, $type,
+ $add_path, $strip_path);
+ }
+ // }}}
+
+ // {{{ protected methods
+ /**
+ * Generate ETag
+ *
+ * @access protected
+ * @return string
+ */
+ function generateETag()
+ {
+ if (!$this->etag) {
+ if ($this->data) {
+ $md5 = md5($this->data);
+ } else {
+ $fst = is_resource($this->handle) ?
+ fstat($this->handle) : stat($this->file);
+ $md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']);
+ }
+ $this->etag = '"' . $md5 . '-' . crc32($md5) . '"';
+ }
+ return $this->etag;
+ }
+
+ /**
+ * Send multiple chunks
+ *
+ * @access protected
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param array $chunks
+ */
+ function sendChunks($chunks)
+ {
+ if (count($chunks) == 1) {
+ return $this->sendChunk(array_shift($chunks));
+ }
+
+ $bound = uniqid('HTTP_DOWNLOAD-', true);
+ $cType = $this->headers['Content-Type'];
+ $this->headers['Content-Type'] =
+ 'multipart/byteranges; boundary=' . $bound;
+ $this->sendHeaders();
+ foreach ($chunks as $chunk){
+ if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) {
+ return $e;
+ }
+ }
+ #echo "\r\n--$bound--\r\n";
+ return true;
+ }
+
+ /**
+ * Send chunk of data
+ *
+ * @access protected
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param array $chunk start and end offset of the chunk to send
+ * @param string $cType actual content type
+ * @param string $bound boundary for multipart/byteranges
+ */
+ function sendChunk($chunk, $cType = null, $bound = null)
+ {
+ list($offset, $lastbyte) = $chunk;
+ $length = ($lastbyte - $offset) + 1;
+
+ if ($length < 1) {
+ return PEAR::raiseError(
+ "Error processing range request: $offset-$lastbyte/$length",
+ HTTP_DOWNLOAD_E_INVALID_REQUEST
+ );
+ }
+
+ $range = $offset . '-' . $lastbyte . '/' . $this->size;
+
+ if (isset($cType, $bound)) {
+ echo "\r\n--$bound\r\n",
+ "Content-Type: $cType\r\n",
+ "Content-Range: bytes $range\r\n\r\n";
+ } else {
+ if ($this->isRangeRequest()) {
+ $this->headers['Content-Range'] = 'bytes '. $range;
+ }
+ $this->sendHeaders();
+ }
+
+ if ($this->data) {
+ while (($length -= $this->bufferSize) > 0) {
+ $this->flush(substr($this->data, $offset, $this->bufferSize));
+ $this->throttleDelay and $this->sleep();
+ $offset += $this->bufferSize;
+ }
+ if ($length) {
+ $this->flush(substr($this->data, $offset, $this->bufferSize + $length));
+ }
+ } else {
+ if (!is_resource($this->handle)) {
+ $this->handle = fopen($this->file, 'rb');
+ }
+ fseek($this->handle, $offset);
+ while (($length -= $this->bufferSize) > 0) {
+ $this->flush(fread($this->handle, $this->bufferSize));
+ $this->throttleDelay and $this->sleep();
+ }
+ if ($length) {
+ $this->flush(fread($this->handle, $this->bufferSize + $length));
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get chunks to send
+ *
+ * @access protected
+ * @return array
+ */
+ function getChunks()
+ {
+ $parts = array();
+ foreach (explode(',', $this->getRanges()) as $chunk){
+ list($o, $e) = explode('-', $chunk);
+ if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) {
+ $e = $this->size - 1;
+ }
+ if (empty($o) && $o !== 0 && $o !== '0') {
+ $o = $this->size - $e;
+ $e = $this->size - 1;
+ }
+ $parts[] = array($o, $e);
+ }
+ return $parts;
+ }
+
+ /**
+ * Check if range is requested
+ *
+ * @access protected
+ * @return bool
+ */
+ function isRangeRequest()
+ {
+ if (!isset($_SERVER['HTTP_RANGE'])) {
+ return false;
+ }
+ return $this->isValidRange();
+ }
+
+ /**
+ * Get range request
+ *
+ * @access protected
+ * @return array
+ */
+ function getRanges()
+ {
+ return preg_match('/^bytes=((\d*-\d*,? ?)+)$/',
+ @$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array();
+ }
+
+ /**
+ * Check if entity is cached
+ *
+ * @access protected
+ * @return bool
+ */
+ function isCached()
+ {
+ return (
+ (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
+ $this->lastModified == strtotime(current($a = explode(
+ ';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) ||
+ (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
+ $this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag))
+ );
+ }
+
+ /**
+ * Check if entity hasn't changed
+ *
+ * @access protected
+ * @return bool
+ */
+ function isValidRange()
+ {
+ if (isset($_SERVER['HTTP_IF_MATCH']) &&
+ !$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) {
+ return false;
+ }
+ if (isset($_SERVER['HTTP_IF_RANGE']) &&
+ $_SERVER['HTTP_IF_RANGE'] !== $this->etag &&
+ strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) {
+ return false;
+ }
+ if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
+ $lm = array_shift(explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']));
+ if (strtotime($lm) !== $this->lastModified) {
+ return false;
+ }
+ }
+ if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) {
+ $lm = array_shift(explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE']));
+ if (strtotime($lm) !== $this->lastModified) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare against an asterisk or check for equality
+ *
+ * @access protected
+ * @return bool
+ * @param string key for the $_SERVER array
+ * @param string string to compare
+ */
+ function compareAsterisk($svar, $compare)
+ {
+ foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) {
+ if ($request === '*' || $request === $compare) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Send HTTP headers
+ *
+ * @access protected
+ * @return void
+ */
+ function sendHeaders()
+ {
+ foreach ($this->headers as $header => $value) {
+ $this->HTTP->setHeader($header, $value);
+ }
+ $this->HTTP->sendHeaders();
+ /* NSAPI won't output anything if we did this */
+ if (strncasecmp(PHP_SAPI, 'nsapi', 5)) {
+ ob_flush();
+ flush();
+ }
+ }
+
+ /**
+ * Flush
+ *
+ * @access protected
+ * @return void
+ * @param string $data
+ */
+ function flush($data = '')
+ {
+ if ($dlen = strlen($data)) {
+ $this->sentBytes += $dlen;
+ echo $data;
+ }
+ ob_flush();
+ flush();
+ }
+
+ /**
+ * Sleep
+ *
+ * @access protected
+ * @return void
+ */
+ function sleep()
+ {
+ if (OS_WINDOWS) {
+ com_message_pump($this->throttleDelay);
+ } else {
+ usleep($this->throttleDelay * 1000);
+ }
+ }
+ // }}}
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Download/Archive.php b/thirdparty/pear/WebDAV/Download/Archive.php
new file mode 100644
index 0000000..2a16995
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Download/Archive.php
@@ -0,0 +1,122 @@
+
+ * @copyright 2003-2005 Michael Wallner
+ * @license BSD, revisewd
+ * @version CVS: $Id: Archive.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+ * @link http://pear.php.net/package/HTTP_Download
+ */
+
+/**
+ * Requires HTTP_Download
+ */
+require_once 'HTTP/Download.php';
+
+/**
+ * Requires System
+ */
+require_once 'System.php';
+
+/**
+ * HTTP_Download_Archive
+ *
+ * Helper class for sending Archives.
+ *
+ * @access public
+ * @version $Revision: 6819 $
+ */
+class HTTP_Download_Archive
+{
+ /**
+ * Send a bunch of files or directories as an archive
+ *
+ * Example:
+ *
+ * require_once 'HTTP/Download/Archive.php';
+ * HTTP_Download_Archive::send(
+ * 'myArchive.tgz',
+ * '/var/ftp/pub/mike',
+ * HTTP_DOWNLOAD_BZ2,
+ * '',
+ * '/var/ftp/pub'
+ * );
+ *
+ *
+ * @see Archive_Tar::createModify()
+ * @static
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $name name the sent archive should have
+ * @param mixed $files files/directories
+ * @param string $type archive type
+ * @param string $add_path path that should be prepended to the files
+ * @param string $strip_path path that should be stripped from the files
+ */
+ function send($name, $files, $type = HTTP_DOWNLOAD_TGZ, $add_path = '', $strip_path = '')
+ {
+ $tmp = System::mktemp();
+
+ switch ($type = strToUpper($type))
+ {
+ case HTTP_DOWNLOAD_TAR:
+ include_once 'Archive/Tar.php';
+ $arc = &new Archive_Tar($tmp);
+ $content_type = 'x-tar';
+ break;
+
+ case HTTP_DOWNLOAD_TGZ:
+ include_once 'Archive/Tar.php';
+ $arc = &new Archive_Tar($tmp, 'gz');
+ $content_type = 'x-gzip';
+ break;
+
+ case HTTP_DOWNLOAD_BZ2:
+ include_once 'Archive/Tar.php';
+ $arc = &new Archive_Tar($tmp, 'bz2');
+ $content_type = 'x-bzip2';
+ break;
+
+ case HTTP_DOWNLOAD_ZIP:
+ include_once 'Archive/Zip.php';
+ $arc = &new Archive_Zip($tmp);
+ $content_type = 'x-zip';
+ break;
+
+ default:
+ return PEAR::raiseError(
+ 'Archive type not supported: ' . $type,
+ HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE
+ );
+ }
+
+ if ($type == HTTP_DOWNLOAD_ZIP) {
+ $options = array( 'add_path' => $add_path,
+ 'remove_path' => $strip_path);
+ if (!$arc->create($files, $options)) {
+ return PEAR::raiseError('Archive creation failed.');
+ }
+ } else {
+ if (!$e = $arc->createModify($files, $add_path, $strip_path)) {
+ return PEAR::raiseError('Archive creation failed.');
+ }
+ if (PEAR::isError($e)) {
+ return $e;
+ }
+ }
+ unset($arc);
+
+ $dl = &new HTTP_Download(array('file' => $tmp));
+ $dl->setContentType('application/' . $content_type);
+ $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, $name);
+ return $dl->send();
+ }
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Download/PgLOB.php b/thirdparty/pear/WebDAV/Download/PgLOB.php
new file mode 100644
index 0000000..e151919
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Download/PgLOB.php
@@ -0,0 +1,177 @@
+
+ * @copyright 2003-2005 Michael Wallner
+ * @license BSD, revised
+ * @version CVS: $Id: PgLOB.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+ * @link http://pear.php.net/package/HTTP_Download
+ */
+
+$GLOBALS['_HTTP_Download_PgLOB_Connection'] = null;
+stream_register_wrapper('pglob', 'HTTP_Download_PgLOB');
+
+/**
+ * PgSQL large object stream interface for HTTP_Download
+ *
+ * Usage:
+ *
+ * require_once 'HTTP/Download.php';
+ * require_once 'HTTP/Download/PgLOB.php';
+ * $db = &DB::connect('pgsql://user:pass@host/db');
+ * // or $db = pg_connect(...);
+ * $lo = HTTP_Download_PgLOB::open($db, 12345);
+ * $dl = &new HTTP_Download;
+ * $dl->setResource($lo);
+ * $dl->send()
+ *
+ *
+ * @access public
+ * @version $Revision: 6819 $
+ */
+class HTTP_Download_PgLOB
+{
+ /**
+ * Set Connection
+ *
+ * @static
+ * @access public
+ * @return bool
+ * @param mixed $conn
+ */
+ function setConnection($conn)
+ {
+ if (is_a($conn, 'DB_Common')) {
+ $conn = $conn->dbh;
+ } elseif ( is_a($conn, 'MDB_Common') ||
+ is_a($conn, 'MDB2_Driver_Common')) {
+ $conn = $conn->connection;
+ }
+ if ($isResource = is_resource($conn)) {
+ $GLOBALS['_HTTP_Download_PgLOB_Connection'] = $conn;
+ }
+ return $isResource;
+ }
+
+ /**
+ * Get Connection
+ *
+ * @static
+ * @access public
+ * @return resource
+ */
+ function getConnection()
+ {
+ if (is_resource($GLOBALS['_HTTP_Download_PgLOB_Connection'])) {
+ return $GLOBALS['_HTTP_Download_PgLOB_Connection'];
+ }
+ return null;
+ }
+
+ /**
+ * Open
+ *
+ * @static
+ * @access public
+ * @return resource
+ * @param mixed $conn
+ * @param int $loid
+ * @param string $mode
+ */
+ function open($conn, $loid, $mode = 'rb')
+ {
+ HTTP_Download_PgLOB::setConnection($conn);
+ return fopen('pglob:///'. $loid, $mode);
+ }
+
+ /**#@+
+ * Stream Interface Implementation
+ * @internal
+ */
+ var $ID = 0;
+ var $size = 0;
+ var $conn = null;
+ var $handle = null;
+
+ function stream_open($path, $mode)
+ {
+ if (!$this->conn = HTTP_Download_PgLOB::getConnection()) {
+ return false;
+ }
+ if (!preg_match('/(\d+)/', $path, $matches)) {
+ return false;
+ }
+ $this->ID = $matches[1];
+
+ if (!pg_query($this->conn, 'BEGIN')) {
+ return false;
+ }
+
+ $this->handle = pg_lo_open($this->conn, $this->ID, $mode);
+ if (!is_resource($this->handle)) {
+ return false;
+ }
+
+ // fetch size of lob
+ pg_lo_seek($this->handle, 0, PGSQL_SEEK_END);
+ $this->size = (int) pg_lo_tell($this->handle);
+ pg_lo_seek($this->handle, 0, PGSQL_SEEK_SET);
+
+ return true;
+ }
+
+ function stream_read($length)
+ {
+ return pg_lo_read($this->handle, $length);
+ }
+
+ function stream_seek($offset, $whence = SEEK_SET)
+ {
+ return pg_lo_seek($this->handle, $offset, $whence);
+ }
+
+ function stream_tell()
+ {
+ return pg_lo_tell($this->handle);
+ }
+
+ function stream_eof()
+ {
+ return pg_lo_tell($this->handle) >= $this->size;
+ }
+
+ function stream_flush()
+ {
+ return true;
+ }
+
+ function stream_stat()
+ {
+ return array('size' => $this->size, 'ino' => $this->ID);
+ }
+
+ function stream_write($data)
+ {
+ return pg_lo_write($this->handle, $data);
+ }
+
+ function stream_close()
+ {
+ if (pg_lo_close($this->handle)) {
+ return pg_query($this->conn, 'COMMIT');
+ } else {
+ pg_query($this->conn ,'ROLLBACK');
+ return false;
+ }
+ }
+ /**#@-*/
+}
+
+?>
diff --git a/thirdparty/pear/WebDAV/Header.php b/thirdparty/pear/WebDAV/Header.php
new file mode 100644
index 0000000..559ae6d
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Header.php
@@ -0,0 +1,531 @@
+
+ * @author Davey Shafik
+ * @author Michael Wallner
+ * @copyright 2003-2005 The Authors
+ * @license BSD, revised
+ * @version CVS: $Id: Header.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+ * @link http://pear.php.net/package/HTTP_Header
+ */
+
+/**
+ * Requires HTTP
+ */
+require_once 'HTTP.php';
+
+/**#@+
+ * Information Codes
+ */
+define('HTTP_HEADER_STATUS_100', '100 Continue');
+define('HTTP_HEADER_STATUS_101', '101 Switching Protocols');
+define('HTTP_HEADER_STATUS_102', '102 Processing');
+define('HTTP_HEADER_STATUS_INFORMATIONAL',1);
+/**#@-*/
+
+/**#+
+ * Success Codes
+ */
+define('HTTP_HEADER_STATUS_200', '200 OK');
+define('HTTP_HEADER_STATUS_201', '201 Created');
+define('HTTP_HEADER_STATUS_202', '202 Accepted');
+define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information');
+define('HTTP_HEADER_STATUS_204', '204 No Content');
+define('HTTP_HEADER_STATUS_205', '205 Reset Content');
+define('HTTP_HEADER_STATUS_206', '206 Partial Content');
+define('HTTP_HEADER_STATUS_207', '207 Multi-Status');
+define('HTTP_HEADER_STATUS_SUCCESSFUL',2);
+/**#@-*/
+
+/**#@+
+ * Redirection Codes
+ */
+define('HTTP_HEADER_STATUS_300', '300 Multiple Choices');
+define('HTTP_HEADER_STATUS_301', '301 Moved Permanently');
+define('HTTP_HEADER_STATUS_302', '302 Found');
+define('HTTP_HEADER_STATUS_303', '303 See Other');
+define('HTTP_HEADER_STATUS_304', '304 Not Modified');
+define('HTTP_HEADER_STATUS_305', '305 Use Proxy');
+define('HTTP_HEADER_STATUS_306', '306 (Unused)');
+define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect');
+define('HTTP_HEADER_STATUS_REDIRECT',3);
+/**#@-*/
+
+/**#@+
+ * Error Codes
+ */
+define('HTTP_HEADER_STATUS_400', '400 Bad Request');
+define('HTTP_HEADER_STATUS_401', '401 Unauthorized');
+define('HTTP_HEADER_STATUS_402', '402 Payment Granted');
+define('HTTP_HEADER_STATUS_403', '403 Forbidden');
+define('HTTP_HEADER_STATUS_404', '404 File Not Found');
+define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed');
+define('HTTP_HEADER_STATUS_406', '406 Not Acceptable');
+define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required');
+define('HTTP_HEADER_STATUS_408', '408 Request Time-out');
+define('HTTP_HEADER_STATUS_409', '409 Conflict');
+define('HTTP_HEADER_STATUS_410', '410 Gone');
+define('HTTP_HEADER_STATUS_411', '411 Length Required');
+define('HTTP_HEADER_STATUS_412', '412 Precondition Failed');
+define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large');
+define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large');
+define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type');
+define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable');
+define('HTTP_HEADER_STATUS_417', '417 Expectation Failed');
+define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity');
+define('HTTP_HEADER_STATUS_423', '423 Locked');
+define('HTTP_HEADER_STATUS_424', '424 Failed Dependency');
+define('HTTP_HEADER_STATUS_CLIENT_ERROR',4);
+/**#@-*/
+
+/**#@+
+ * Server Errors
+ */
+define('HTTP_HEADER_STATUS_500', '500 Internal Server Error');
+define('HTTP_HEADER_STATUS_501', '501 Not Implemented');
+define('HTTP_HEADER_STATUS_502', '502 Bad Gateway');
+define('HTTP_HEADER_STATUS_503', '503 Service Unavailable');
+define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out');
+define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported');
+define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage');
+define('HTTP_HEADER_STATUS_SERVER_ERROR',5);
+/**#@-*/
+
+/**
+ * HTTP_Header
+ *
+ * @package HTTP_Header
+ * @category HTTP
+ * @access public
+ * @version $Revision: 6819 $
+ */
+class HTTP_Header extends HTTP
+{
+ /**
+ * Default Headers
+ *
+ * The values that are set as default, are the same as PHP sends by default.
+ *
+ * @var array
+ * @access private
+ */
+ var $_headers = array(
+ 'content-type' => 'text/html',
+ 'pragma' => 'no-cache',
+ 'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
+ );
+
+ /**
+ * HTTP version
+ *
+ * @var string
+ * @access private
+ */
+ var $_httpVersion = '1.0';
+
+ /**
+ * Constructor
+ *
+ * Sets HTTP version.
+ *
+ * @access public
+ * @return object HTTP_Header
+ */
+ function HTTP_Header()
+ {
+ if (isset($_SERVER['SERVER_PROTOCOL'])) {
+ $this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3));
+ }
+ }
+
+ /**
+ * Set HTTP version
+ *
+ * @access public
+ * @return bool Returns true on success or false if version doesn't
+ * match 1.0 or 1.1 (note: 1 will result in 1.0)
+ * @param mixed $version HTTP version, either 1.0 or 1.1
+ */
+ function setHttpVersion($version)
+ {
+ $version = round((float) $version, 1);
+ if ($version < 1.0 || $version > 1.1) {
+ return false;
+ }
+ $this->_httpVersion = sprintf('%0.1f', $version);
+ return true;
+ }
+
+ /**
+ * Get HTTP version
+ *
+ * @access public
+ * @return string
+ */
+ function getHttpVersion()
+ {
+ return $this->_httpVersion;
+ }
+
+ /**
+ * Set Header
+ *
+ * The default value for the Last-Modified header will be current
+ * date and atime if $value is omitted.
+ *
+ * @access public
+ * @return bool Returns true on success or false if $key was empty or
+ * $value was not of an scalar type.
+ * @param string $key The name of the header.
+ * @param string $value The value of the header. (NULL to unset header)
+ */
+ function setHeader($key, $value = null)
+ {
+ if (empty($key) || (isset($value) && !is_scalar($value))) {
+ return false;
+ }
+
+ $key = strToLower($key);
+ if ($key == 'last-modified') {
+ if (!isset($value)) {
+ $value = HTTP::Date(time());
+ } elseif (is_numeric($value)) {
+ $value = HTTP::Date($value);
+ }
+ }
+
+ if (isset($value)) {
+ $this->_headers[$key] = $value;
+ } else {
+ unset($this->_headers[$key]);
+ }
+
+ return true;
+ }
+
+ /**
+ * Get Header
+ *
+ * If $key is omitted, all stored headers will be returned.
+ *
+ * @access public
+ * @return mixed Returns string value of the requested header,
+ * array values of all headers or false if header $key
+ * is not set.
+ * @param string $key The name of the header to fetch.
+ */
+ function getHeader($key = null)
+ {
+ if (!isset($key)) {
+ return $this->_headers;
+ }
+
+ $key = strToLower($key);
+
+ if (!isset($this->_headers[$key])) {
+ return false;
+ }
+
+ return $this->_headers[$key];
+ }
+
+ /**
+ * Send Headers
+ *
+ * Send out the header that you set via setHeader().
+ *
+ * @access public
+ * @return bool Returns true on success or false if headers are already
+ * sent.
+ * @param array $keys Headers to (not) send, see $include.
+ * @param array $include If true only $keys matching headers will be
+ * sent, if false only header not matching $keys will be
+ * sent.
+ */
+ function sendHeaders($keys = array(), $include = true)
+ {
+ if (headers_sent()) {
+ return false;
+ }
+
+ if (count($keys)) {
+ array_change_key_case($keys, CASE_LOWER);
+ foreach ($this->_headers as $key => $value) {
+ if ($include ? in_array($key, $keys) : !in_array($key, $keys)) {
+ header($key .': '. $value);
+ }
+ }
+ } else {
+ foreach ($this->_headers as $header => $value) {
+ header($header .': '. $value);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Send Satus Code
+ *
+ * Send out the given HTTP-Status code. Use this for example when you
+ * want to tell the client this page is cached, then you would call
+ * sendStatusCode(304).
+ *
+ * @see HTTP_Header_Cache::exitIfCached()
+ *
+ * @access public
+ * @return bool Returns true on success or false if headers are already
+ * sent.
+ * @param int $code The status code to send, i.e. 404, 304, 200, etc.
+ */
+ function sendStatusCode($code)
+ {
+ if (headers_sent()) {
+ return false;
+ }
+
+ if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) {
+ $code = constant('HTTP_HEADER_STATUS_'. $code);
+ }
+
+ if (strncasecmp(PHP_SAPI, 'cgi', 3)) {
+ header('HTTP/'. $this->_httpVersion .' '. $code);
+ } else {
+ header('Status: '. $code);
+ }
+ return true;
+ }
+
+ /**
+ * Date to Timestamp
+ *
+ * Converts dates like
+ * Mon, 31 Mar 2003 15:26:34 GMT
+ * Tue, 15 Nov 1994 12:45:26 GMT
+ * into a timestamp, strtotime() didn't do it in older versions.
+ *
+ * @deprecated Use PHPs strtotime() instead.
+ * @access public
+ * @return mixed Returns int unix timestamp or false if the date doesn't
+ * seem to be a valid GMT date.
+ * @param string $date The GMT date.
+ */
+ function dateToTimestamp($date)
+ {
+ static $months = array(
+ null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
+ 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9,
+ 'Oct' => 10, 'Nov' => 11, 'Dec' => 12
+ );
+
+ if (-1 < $timestamp = strToTime($date)) {
+ return $timestamp;
+ }
+
+ if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~',
+ $date, $m)) {
+ return false;
+ }
+
+ // [0] => Mon, 31 Mar 2003 15:42:55 GMT
+ // [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55
+ return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]);
+ }
+
+ /**
+ * Redirect
+ *
+ * This function redirects the client. This is done by issuing a Location
+ * header and exiting. Additionally to HTTP::redirect() you can also add
+ * parameters to the url.
+ *
+ * If you dont need parameters to be added, simply use HTTP::redirect()
+ * otherwise use HTTP_Header::redirect().
+ *
+ * @see HTTP::redirect()
+ * @author Wolfram Kriesing
+ * @access public
+ * @return void
+ * @param string $url The URL to redirect to, if none is given it
+ * redirects to the current page.
+ * @param array $param Array of query string parameters to add; usually
+ * a set of key => value pairs; if an array entry consists
+ * only of an value it is used as key and the respective
+ * value is fetched from $GLOBALS[$value]
+ * @param bool $session Whether the session name/id should be added
+ */
+ function redirect($url = null, $param = array(), $session = false)
+ {
+ if (!isset($url)) {
+ $url = $_SERVER['PHP_SELF'];
+ }
+
+ $qs = array();
+
+ if ($session) {
+ $qs[] = session_name() .'='. session_id();
+ }
+
+ if (is_array($param) && count($param)) {
+ if (count($param)) {
+ foreach ($param as $key => $val) {
+ if (is_string($key)) {
+ $qs[] = urlencode($key) .'='. urlencode($val);
+ } else {
+ $qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]);
+ }
+ }
+ }
+ }
+
+ if ($qstr = implode('&', $qs)) {
+ $purl = parse_url($url);
+ $url .= (isset($purl['query']) ? '&' : '?') . $qstr;
+ }
+
+ parent::redirect($url);
+ }
+
+ /**#@+
+ * @author Davey Shafik
+ * @param int $http_code HTTP Code to check
+ * @access public
+ */
+
+ /**
+ * Return HTTP Status Code Type
+ *
+ * @return int|false
+ */
+ function getStatusType($http_code)
+ {
+ if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) {
+ $type = substr($http_code,0,1);
+ switch ($type) {
+ case HTTP_HEADER_STATUS_INFORMATIONAL:
+ case HTTP_HEADER_STATUS_SUCCESSFUL:
+ case HTTP_HEADER_STATUS_REDIRECT:
+ case HTTP_HEADER_STATUS_CLIENT_ERROR:
+ case HTTP_HEADER_STATUS_SERVER_ERROR:
+ return $type;
+ break;
+ default:
+ return false;
+ break;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return Status Code Message
+ *
+ * @return string|false
+ */
+ function getStatusText($http_code)
+ {
+ if ($this->getStatusType($http_code)) {
+ if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) {
+ return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4);
+ } else {
+ return substr($http_code,4);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if HTTP Status code is Information (1xx)
+ *
+ * @return boolean
+ */
+ function isInformational($http_code)
+ {
+ if ($status_type = $this->getStatusType($http_code)) {
+ return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if HTTP Status code is Successful (2xx)
+ *
+ * @return boolean
+ */
+ function isSuccessful($http_code)
+ {
+ if ($status_type = $this->getStatusType($http_code)) {
+ return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if HTTP Status code is a Redirect (3xx)
+ *
+ * @return boolean
+ */
+ function isRedirect($http_code)
+ {
+ if ($status_type = $this->getStatusType($http_code)) {
+ return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if HTTP Status code is a Client Error (4xx)
+ *
+ * @return boolean
+ */
+ function isClientError($http_code)
+ {
+ if ($status_type = $this->getStatusType($http_code)) {
+ return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if HTTP Status code is Server Error (5xx)
+ *
+ * @return boolean
+ */
+ function isServerError($http_code)
+ {
+ if ($status_type = $this->getStatusType($http_code)) {
+ return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
+ *
+ * @return boolean
+ */
+ function isError($http_code)
+ {
+ if ($status_type = $this->getStatusType($http_code)) {
+ return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false;
+ } else {
+ return false;
+ }
+ }
+ /**#@-*/
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Header/Cache.php b/thirdparty/pear/WebDAV/Header/Cache.php
new file mode 100644
index 0000000..82891d4
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Header/Cache.php
@@ -0,0 +1,238 @@
+
+ * @author Michael Wallner
+ * @copyright 2003-2005 The Authors
+ * @license BSD, revised
+ * @version CVS: $Id: Cache.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+ * @link http://pear.php.net/package/HTTP_Header
+ */
+
+/**
+ * Requires HTTP_Header
+ */
+require_once 'HTTP/Header.php';
+
+/**
+ * HTTP_Header_Cache
+ *
+ * This package provides methods to easier handle caching of HTTP pages. That
+ * means that the pages can be cached at the client (user agent or browser) and
+ * your application only needs to send "hey client you already have the pages".
+ *
+ * Which is done by sending the HTTP-Status "304 Not Modified", so that your
+ * application load and the network traffic can be reduced, since you only need
+ * to send the complete page once. This is really an advantage e.g. for
+ * generated style sheets, or simply pages that do only change rarely.
+ *
+ * Usage:
+ *
+ * require_once 'HTTP/Header/Cache.php';
+ * $httpCache = new HTTP_Header_Cache(4, 'weeks');
+ * $httpCache->sendHeaders();
+ * // your code goes here
+ *
+ *
+ * @package HTTP_Header
+ * @category HTTP
+ * @access public
+ * @version $Revision: 6819 $
+ */
+class HTTP_Header_Cache extends HTTP_Header
+{
+ /**
+ * Constructor
+ *
+ * Set the amount of time to cache.
+ *
+ * @access public
+ * @return object HTTP_Header_Cache
+ * @param int $expires
+ * @param string $unit
+ */
+ function HTTP_Header_Cache($expires = 0, $unit = 'seconds')
+ {
+ parent::HTTP_Header();
+ $this->setHeader('Pragma', 'cache');
+ $this->setHeader('Last-Modified', $this->getCacheStart());
+ $this->setHeader('Cache-Control', 'private, must-revalidate, max-age=0');
+
+ if ($expires) {
+ if (!$this->isOlderThan($expires, $unit)) {
+ $this->exitCached();
+ }
+ $this->setHeader('Last-Modified', time());
+ }
+ }
+
+ /**
+ * Get Cache Start
+ *
+ * Returns the unix timestamp of the If-Modified-Since HTTP header or the
+ * current time if the header was not sent by the client.
+ *
+ * @access public
+ * @return int unix timestamp
+ */
+ function getCacheStart()
+ {
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$this->isPost()) {
+ return strtotime(current($array = explode(';',
+ $_SERVER['HTTP_IF_MODIFIED_SINCE'])));
+ }
+ return time();
+ }
+
+ /**
+ * Is Older Than
+ *
+ * You can call it like this:
+ *
+ * $httpCache->isOlderThan(1, 'day');
+ * $httpCache->isOlderThan(47, 'days');
+ *
+ * $httpCache->isOlderThan(1, 'week');
+ * $httpCache->isOlderThan(3, 'weeks');
+ *
+ * $httpCache->isOlderThan(1, 'hour');
+ * $httpCache->isOlderThan(5, 'hours');
+ *
+ * $httpCache->isOlderThan(1, 'minute');
+ * $httpCache->isOlderThan(15, 'minutes');
+ *
+ * $httpCache->isOlderThan(1, 'second');
+ * $httpCache->isOlderThan(15);
+ *
+ *
+ * If you specify something greater than "weeks" as time untit, it just
+ * works approximatly, because a month is taken to consist of 4.3 weeks.
+ *
+ * @access public
+ * @return bool Returns true if requested page is older than specified.
+ * @param int $time The amount of time.
+ * @param string $unit The unit of the time amount - (year[s], month[s],
+ * week[s], day[s], hour[s], minute[s], second[s]).
+ */
+ function isOlderThan($time = 0, $unit = 'seconds')
+ {
+ if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || $this->isPost()) {
+ return true;
+ }
+ if (!$time) {
+ return false;
+ }
+
+ switch (strtolower($unit))
+ {
+ case 'year':
+ case 'years':
+ $time *= 12;
+ case 'month':
+ case 'months':
+ $time *= 4.3;
+ case 'week':
+ case 'weeks':
+ $time *= 7;
+ case 'day':
+ case 'days':
+ $time *= 24;
+ case 'hour':
+ case 'hours':
+ $time *= 60;
+ case 'minute':
+ case 'minutes':
+ $time *= 60;
+ }
+
+ return (time() - $this->getCacheStart()) > $time;
+ }
+
+ /**
+ * Is Cached
+ *
+ * Check whether we can consider to be cached on the client side.
+ *
+ * @access public
+ * @return bool Whether the page/resource is considered to be cached.
+ * @param int $lastModified Unix timestamp of last modification.
+ */
+ function isCached($lastModified = 0)
+ {
+ if ($this->isPost()) {
+ return false;
+ }
+ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$lastModified) {
+ return true;
+ }
+ if (!$seconds = time() - $lastModified) {
+ return false;
+ }
+ return !$this->isOlderThan($seconds);
+ }
+
+ /**
+ * Is Post
+ *
+ * Check if request method is "POST".
+ *
+ * @access public
+ * @return bool
+ */
+ function isPost()
+ {
+ return isset($_SERVER['REQUEST_METHOD']) and
+ 'POST' == $_SERVER['REQUEST_METHOD'];
+ }
+
+ /**
+ * Exit If Cached
+ *
+ * Exit with "HTTP 304 Not Modified" if we consider to be cached.
+ *
+ * @access public
+ * @return void
+ * @param int $lastModified Unix timestamp of last modification.
+ */
+ function exitIfCached($lastModified = 0)
+ {
+ if ($this->isCached($lastModified)) {
+ $this->exitCached();
+ }
+ }
+
+ /**
+ * Exit Cached
+ *
+ * Exit with "HTTP 304 Not Modified".
+ *
+ * @access public
+ * @return void
+ */
+ function exitCached()
+ {
+ $this->sendHeaders();
+ $this->sendStatusCode(304);
+ exit;
+ }
+
+ /**
+ * Set Last Modified
+ *
+ * @access public
+ * @return void
+ * @param int $lastModified The unix timestamp of last modification.
+ */
+ function setLastModified($lastModified = null)
+ {
+ $this->setHeader('Last-Modified', $lastModified);
+ }
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Request.php b/thirdparty/pear/WebDAV/Request.php
new file mode 100644
index 0000000..4584dba
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Request.php
@@ -0,0 +1,1191 @@
+ |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Request.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+//
+// HTTP_Request Class
+//
+// Simple example, (Fetches yahoo.com and displays it):
+//
+// $a = &new HTTP_Request('http://www.yahoo.com/');
+// $a->sendRequest();
+// echo $a->getResponseBody();
+//
+
+require_once 'PEAR.php';
+require_once 'Net/Socket.php';
+require_once 'Net/URL.php';
+
+define('HTTP_REQUEST_METHOD_GET', 'GET', true);
+define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
+define('HTTP_REQUEST_METHOD_POST', 'POST', true);
+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);
+
+define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
+define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
+
+class HTTP_Request {
+
+ /**
+ * Instance of Net_URL
+ * @var object Net_URL
+ */
+ var $_url;
+
+ /**
+ * Type of request
+ * @var string
+ */
+ var $_method;
+
+ /**
+ * HTTP Version
+ * @var string
+ */
+ var $_http;
+
+ /**
+ * Request headers
+ * @var array
+ */
+ var $_requestHeaders;
+
+ /**
+ * Basic Auth Username
+ * @var string
+ */
+ var $_user;
+
+ /**
+ * Basic Auth Password
+ * @var string
+ */
+ var $_pass;
+
+ /**
+ * Socket object
+ * @var object 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
+ */
+ var $_proxy_pass;
+
+ /**
+ * Post data
+ * @var array
+ */
+ var $_postData;
+
+ /**
+ * Request body
+ * @var string
+ */
+ var $_body;
+
+ /**
+ * A list of methods that MUST NOT have a request body, per RFC 2616
+ * @var array
+ */
+ var $_bodyDisallowed = array('TRACE');
+
+ /**
+ * Files to post
+ * @var array
+ */
+ var $_postFiles = array();
+
+ /**
+ * Connection timeout.
+ * @var float
+ */
+ var $_timeout;
+
+ /**
+ * HTTP_Response object
+ * @var object 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
+ */
+ var $_redirects;
+
+ /**
+ * Whether to append brackets [] to array variables
+ * @var bool
+ */
+ var $_useBrackets = true;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Whether to save response body in response object property
+ * @var bool
+ */
+ var $_saveBody = true;
+
+ /**
+ * Timeout for reading from socket (array(seconds, microseconds))
+ * @var array
+ */
+ var $_readTimeout = null;
+
+ /**
+ * Options to pass to Net_Socket::connect. See stream_context_create
+ * @var array
+ */
+ var $_socketOptions = null;
+
+ /**
+ * Constructor
+ *
+ * Sets up the object
+ * @param string The url to fetch/access
+ * @param array Associative array of parameters which can have the following keys:
+ *
+ * method - Method to use, GET, POST etc (string)
+ * http - HTTP Version to use, 1.0 or 1.1 (string)
+ * user - Basic Auth username (string)
+ * pass - Basic Auth password (string)
+ * proxy_host - Proxy server host (string)
+ * proxy_port - Proxy server port (integer)
+ * proxy_user - Proxy auth username (string)
+ * proxy_pass - Proxy auth password (string)
+ * timeout - Connection timeout in seconds (float)
+ * allowRedirects - Whether to follow redirects or not (bool)
+ * maxRedirects - Max number of redirects to follow (integer)
+ * useBrackets - Whether to append [] to array variable names (bool)
+ * saveBody - Whether to save response body in response object property (bool)
+ * readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))
+ * socketOptions - Options to pass to Net_Socket object (array)
+ *
+ * @access public
+ */
+ 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();
+ $this->_postData = array();
+ $this->_body = null;
+
+ $this->_user = null;
+ $this->_pass = null;
+
+ $this->_proxy_host = null;
+ $this->_proxy_port = null;
+ $this->_proxy_user = null;
+ $this->_proxy_pass = null;
+
+ $this->_allowRedirects = false;
+ $this->_maxRedirects = 3;
+ $this->_redirects = 0;
+
+ $this->_timeout = null;
+ $this->_response = null;
+
+ foreach ($params as $key => $value) {
+ $this->{'_' . $key} = $value;
+ }
+
+ if (!empty($url)) {
+ $this->setURL($url);
+ }
+
+ // Default useragent
+ $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
+
+ // Make sure keepalives dont knobble us
+ $this->addHeader('Connection', 'close');
+
+ // Basic authentication
+ if (!empty($this->_user)) {
+ $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'))) {
+
+ $this->addHeader('Accept-Encoding', 'gzip');
+ }
+ }
+
+ /**
+ * Generates a Host header for HTTP/1.1 requests
+ *
+ * @access private
+ * @return string
+ */
+ function _generateHostHeader()
+ {
+ if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } 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.
+ *
+ * @param string $url The url to be requested
+ * @param array $params Associative array of parameters
+ * (see constructor for details)
+ * @access public
+ * @deprecated deprecated since 1.2, call the constructor if this is necessary
+ */
+ function reset($url, $params = array())
+ {
+ $this->HTTP_Request($url, $params);
+ }
+
+ /**
+ * Sets the URL to be requested
+ *
+ * @param string The url to be requested
+ * @access public
+ */
+ function setURL($url)
+ {
+ $this->_url = &new Net_URL($url, $this->_useBrackets);
+
+ if (!empty($this->_url->user) || !empty($this->_url->pass)) {
+ $this->setBasicAuth($this->_url->user, $this->_url->pass);
+ }
+
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
+ $this->addHeader('Host', $this->_generateHostHeader());
+ }
+ }
+
+ /**
+ * Sets a proxy to be used
+ *
+ * @param string Proxy host
+ * @param int Proxy port
+ * @param string Proxy username
+ * @param string Proxy password
+ * @access public
+ */
+ function setProxy($host, $port = 8080, $user = null, $pass = null)
+ {
+ $this->_proxy_host = $host;
+ $this->_proxy_port = $port;
+ $this->_proxy_user = $user;
+ $this->_proxy_pass = $pass;
+
+ if (!empty($user)) {
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+ }
+
+ /**
+ * Sets basic authentication parameters
+ *
+ * @param string Username
+ * @param string Password
+ */
+ function setBasicAuth($user, $pass)
+ {
+ $this->_user = $user;
+ $this->_pass = $pass;
+
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+
+ /**
+ * Sets the method to be used, GET, POST etc.
+ *
+ * @param string Method to use. Use the defined constants for this
+ * @access public
+ */
+ function setMethod($method)
+ {
+ $this->_method = $method;
+ }
+
+ /**
+ * Sets the HTTP version to use, 1.0 or 1.1
+ *
+ * @param string Version to use. Use the defined constants for this
+ * @access public
+ */
+ function setHttpVer($http)
+ {
+ $this->_http = $http;
+ }
+
+ /**
+ * Adds a request header
+ *
+ * @param string Header name
+ * @param string Header value
+ * @access public
+ */
+ function addHeader($name, $value)
+ {
+ $this->_requestHeaders[strtolower($name)] = $value;
+ }
+
+ /**
+ * Removes a request header
+ *
+ * @param string Header name to remove
+ * @access public
+ */
+ function removeHeader($name)
+ {
+ if (isset($this->_requestHeaders[strtolower($name)])) {
+ unset($this->_requestHeaders[strtolower($name)]);
+ }
+ }
+
+ /**
+ * Adds a querystring parameter
+ *
+ * @param string Querystring parameter name
+ * @param string Querystring parameter value
+ * @param bool Whether the value is already urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ $this->_url->addQueryString($name, $value, $preencoded);
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string The querystring data. Should be of the format foo=bar&x=y etc
+ * @param bool Whether data is already urlencoded or not, default = already encoded
+ * @access public
+ */
+ function addRawQueryString($querystring, $preencoded = true)
+ {
+ $this->_url->addRawQueryString($querystring, $preencoded);
+ }
+
+ /**
+ * Adds postdata items
+ *
+ * @param string Post data name
+ * @param string Post data value
+ * @param bool Whether data is already urlencoded or not, default = not
+ * @access public
+ */
+ function addPostData($name, $value, $preencoded = false)
+ {
+ if ($preencoded) {
+ $this->_postData[$name] = $value;
+ } else {
+ $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
+ }
+ }
+
+ /**
+ * Recursively applies the callback function to the value
+ *
+ * @param mixed Callback function
+ * @param mixed Value to process
+ * @access private
+ * @return mixed Processed value
+ */
+ function _arrayMapRecursive($callback, $value)
+ {
+ if (!is_array($value)) {
+ return call_user_func($callback, $value);
+ } else {
+ $map = array();
+ foreach ($value as $k => $v) {
+ $map[$k] = $this->_arrayMapRecursive($callback, $v);
+ }
+ return $map;
+ }
+ }
+
+ /**
+ * Adds a file to upload
+ *
+ * This also changes content-type to 'multipart/form-data' for proper upload
+ *
+ * @access public
+ * @param string name of file-upload field
+ * @param mixed file name(s)
+ * @param mixed content-type(s) of file(s) being uploaded
+ * @return bool true on success
+ * @throws PEAR_Error
+ */
+ function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
+ {
+ if (!is_array($fileName) && !is_readable($fileName)) {
+ return PEAR::raiseError("File '{$fileName}' is not readable");
+ } elseif (is_array($fileName)) {
+ foreach ($fileName as $name) {
+ if (!is_readable($name)) {
+ return PEAR::raiseError("File '{$name}' is not readable");
+ }
+ }
+ }
+ $this->addHeader('Content-Type', 'multipart/form-data');
+ $this->_postFiles[$inputName] = array(
+ 'name' => $fileName,
+ 'type' => $contentType
+ );
+ return true;
+ }
+
+ /**
+ * Adds raw postdata (DEPRECATED)
+ *
+ * @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
+ */
+ function addRawPostData($postdata, $preencoded = true)
+ {
+ $this->_body = $preencoded ? $postdata : urlencode($postdata);
+ }
+
+ /**
+ * Sets the request body (for POST, PUT and similar requests)
+ *
+ * @param string Request body
+ * @access public
+ */
+ function setBody($body)
+ {
+ $this->_body = $body;
+ }
+
+ /**
+ * Clears any postdata that has been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios.
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearPostData()
+ {
+ $this->_postData = null;
+ }
+
+ /**
+ * Appends a cookie to "Cookie:" header
+ *
+ * @param string $name cookie name
+ * @param string $value cookie value
+ * @access public
+ */
+ function addCookie($name, $value)
+ {
+ $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
+ $this->addHeader('Cookie', $cookies . $name . '=' . $value);
+ }
+
+ /**
+ * Clears any cookies that have been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearCookies()
+ {
+ $this->removeHeader('Cookie');
+ }
+
+ /**
+ * Sends the request
+ *
+ * @access public
+ * @param bool Whether to store response body in Response object property,
+ * set this to false if downloading a LARGE file and using a Listener
+ * @return mixed PEAR error on error, true otherwise
+ */
+ function sendRequest($saveBody = true)
+ {
+ if (!is_a($this->_url, 'Net_URL')) {
+ return PEAR::raiseError('No URL given.');
+ }
+
+ $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.');
+ }
+ $host = 'ssl://' . $host;
+ }
+
+ // magic quotes may fuck up file uploads and chunked response processing
+ $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);
+ PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
+
+ if (!PEAR::isError($err)) {
+ if (!empty($this->_readTimeout)) {
+ $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
+ }
+
+ $this->_notify('sentRequest');
+
+ // Read the response
+ $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
+ $err = $this->_response->process($this->_saveBody && $saveBody);
+ }
+
+ ini_set('magic_quotes_runtime', $magicQuotes);
+
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+
+ // Check for redirection
+ if ( $this->_allowRedirects
+ AND $this->_redirects <= $this->_maxRedirects
+ AND $this->getResponseCode() > 300
+ AND $this->getResponseCode() < 399
+ AND !empty($this->_response->_headers['location'])) {
+
+
+ $redirect = $this->_response->_headers['location'];
+
+ // Absolute URL
+ if (preg_match('/^https?:\/\//i', $redirect)) {
+ $this->_url = &new Net_URL($redirect);
+ $this->addHeader('Host', $this->_generateHostHeader());
+ // 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) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $redirect = Net_URL::resolvePath($redirect);
+ $this->_url->path = $redirect;
+
+ // Filename, no path
+ } else {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $this->_url->path = $redirect;
+ }
+
+ $this->_redirects++;
+ return $this->sendRequest($saveBody);
+
+ // Too many redirects
+ } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
+ return PEAR::raiseError('Too many redirects');
+ }
+
+ $this->_sock->disconnect();
+
+ return true;
+ }
+
+ /**
+ * Returns the response code
+ *
+ * @access public
+ * @return mixed Response code, false if not set
+ */
+ function getResponseCode()
+ {
+ return isset($this->_response->_code) ? $this->_response->_code : false;
+ }
+
+ /**
+ * Returns either the named header or all if no name given
+ *
+ * @access public
+ * @param string The header name to return, do not set to get all headers
+ * @return mixed either the value of $headername (false if header is not present)
+ * or an array of all headers
+ */
+ function getResponseHeader($headername = null)
+ {
+ if (!isset($headername)) {
+ return isset($this->_response->_headers)? $this->_response->_headers: array();
+ } else {
+ $headername = strtolower($headername);
+ return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
+ }
+ }
+
+ /**
+ * Returns the body of the response
+ *
+ * @access public
+ * @return mixed response body, false if not set
+ */
+ function getResponseBody()
+ {
+ return isset($this->_response->_body) ? $this->_response->_body : false;
+ }
+
+ /**
+ * Returns cookies set in response
+ *
+ * @access public
+ * @return mixed array of response cookies, false if none are present
+ */
+ function getResponseCookies()
+ {
+ return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
+ }
+
+ /**
+ * Builds the request string
+ *
+ * @access private
+ * @return string The request string
+ */
+ function _buildRequest()
+ {
+ $separator = ini_get('arg_separator.output');
+ ini_set('arg_separator.output', '&');
+ $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
+ ini_set('arg_separator.output', $separator);
+
+ $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;
+ $url = $host . $port . $path;
+
+ $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))) {
+
+ $this->removeHeader('Content-Type');
+ } else {
+ if (empty($this->_requestHeaders['content-type'])) {
+ // Add default content-type
+ $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
+ } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
+ $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
+ $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
+ }
+ }
+
+ // Request Headers
+ if (!empty($this->_requestHeaders)) {
+ foreach ($this->_requestHeaders as $name => $value) {
+ $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
+ $request .= $canonicalName . ': ' . $value . "\r\n";
+ }
+ }
+
+ // 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))) {
+
+ $request .= "\r\n";
+
+ // Post data if it's an array
+ } 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];'),
+ $this->_flattenArray('', $this->_postData)
+ ));
+
+ // multipart request, probably with file uploads
+ } else {
+ $postdata = '';
+ if (!empty($this->_postData)) {
+ $flatData = $this->_flattenArray('', $this->_postData);
+ foreach ($flatData as $item) {
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
+ $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
+ }
+ }
+ foreach ($this->_postFiles as $name => $value) {
+ if (is_array($value['name'])) {
+ $varname = $name . ($this->_useBrackets? '[]': '');
+ } else {
+ $varname = $name;
+ $value['name'] = array($value['name']);
+ }
+ foreach ($value['name'] as $key => $filename) {
+ $fp = fopen($filename, 'r');
+ $data = fread($fp, filesize($filename));
+ fclose($fp);
+ $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 .= '--' . $boundary . "--\r\n";
+ }
+ $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n";
+ $request .= $postdata;
+
+ // Explicitly set request body
+ } elseif (!empty($this->_body)) {
+
+ $request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n";
+ $request .= $this->_body;
+ }
+
+ return $request;
+ }
+
+ /**
+ * Helper function to change the (probably multidimensional) associative array
+ * into the simple one.
+ *
+ * @param string name for item
+ * @param mixed item's values
+ * @return array array with the following items: array('item name', 'item value');
+ */
+ function _flattenArray($name, $values)
+ {
+ if (!is_array($values)) {
+ return array(array($name, $values));
+ } else {
+ $ret = array();
+ foreach ($values as $k => $v) {
+ if (empty($name)) {
+ $newName = $k;
+ } elseif ($this->_useBrackets) {
+ $newName = $name . '[' . $k . ']';
+ } else {
+ $newName = $name;
+ }
+ $ret = array_merge($ret, $this->_flattenArray($newName, $v));
+ }
+ return $ret;
+ }
+ }
+
+
+ /**
+ * 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
+ * @access public
+ */
+ function attach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
+ return false;
+ }
+ $this->_listeners[$listener->getId()] =& $listener;
+ return true;
+ }
+
+
+ /**
+ * Removes a Listener from the list of listeners
+ *
+ * @param object HTTP_Request_Listener instance to detach
+ * @return boolean whether the listener was successfully detached
+ * @access public
+ */
+ function detach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
+ !isset($this->_listeners[$listener->getId()])) {
+ return false;
+ }
+ unset($this->_listeners[$listener->getId()]);
+ return true;
+ }
+
+
+ /**
+ * 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
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+}
+
+
+/**
+* Response class to complement the Request class
+*/
+class HTTP_Response
+{
+ /**
+ * Socket object
+ * @var object
+ */
+ var $_sock;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $_protocol;
+
+ /**
+ * Return code
+ * @var string
+ */
+ var $_code;
+
+ /**
+ * Response headers
+ * @var array
+ */
+ var $_headers;
+
+ /**
+ * Cookies set in response
+ * @var array
+ */
+ var $_cookies;
+
+ /**
+ * Response body
+ * @var string
+ */
+ var $_body = '';
+
+ /**
+ * Used by _readChunked(): remaining length of the current chunk
+ * @var string
+ */
+ var $_chunkLength = 0;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * 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
+ */
+ function HTTP_Response(&$sock, &$listeners)
+ {
+ $this->_sock =& $sock;
+ $this->_listeners =& $listeners;
+ }
+
+
+ /**
+ * Processes a HTTP response
+ *
+ * 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.
+ * @throws PEAR_Error
+ * @return mixed true on success, PEAR_Error in case of malformed response
+ */
+ function process($saveBody = true)
+ {
+ do {
+ $line = $this->_sock->readLine();
+ if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) {
+ return PEAR::raiseError('Malformed response.');
+ } else {
+ $this->_protocol = 'HTTP/' . $http_version;
+ $this->_code = intval($returncode);
+ }
+ while ('' !== ($header = $this->_sock->readLine())) {
+ $this->_processHeader($header);
+ }
+ } while (100 == $this->_code);
+
+ $this->_notify('gotHeaders', $this->_headers);
+
+ // 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 ($chunked) {
+ $data = $this->_readChunked();
+ } else {
+ $data = $this->_sock->read(4096);
+ }
+ if ('' == $data) {
+ break;
+ } else {
+ $hasBody = true;
+ if ($saveBody || $gzipped) {
+ $this->_body .= $data;
+ }
+ $this->_notify($gzipped? 'gzTick': 'tick', $data);
+ }
+ }
+ }
+ if ($hasBody) {
+ // Uncompress the body if needed
+ if ($gzipped) {
+ $this->_body = gzinflate(substr($this->_body, 10));
+ $this->_notify('gotBody', $this->_body);
+ } else {
+ $this->_notify('gotBody');
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Processes the response header
+ *
+ * @access private
+ * @param string HTTP header
+ */
+ function _processHeader($header)
+ {
+ 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;
+ } else {
+ $this->_headers[$headername] = $headervalue;
+ }
+ } else {
+ $this->_parseCookie($headervalue);
+ }
+ }
+
+
+ /**
+ * Parse a Set-Cookie header to fill $_cookies array
+ *
+ * @access private
+ * @param string value of Set-Cookie header
+ */
+ function _parseCookie($headervalue)
+ {
+ $cookie = array(
+ 'expires' => null,
+ 'domain' => null,
+ 'path' => null,
+ 'secure' => false
+ );
+
+ // Only a name=value pair
+ if (!strpos($headervalue, ';')) {
+ $pos = strpos($headervalue, '=');
+ $cookie['name'] = trim(substr($headervalue, 0, $pos));
+ $cookie['value'] = trim(substr($headervalue, $pos + 1));
+
+ // Some optional parameters are supplied
+ } else {
+ $elements = explode(';', $headervalue);
+ $pos = strpos($elements[0], '=');
+ $cookie['name'] = trim(substr($elements[0], 0, $pos));
+ $cookie['value'] = trim(substr($elements[0], $pos + 1));
+
+ for ($i = 1; $i < count($elements); $i++) {
+ if (false === strpos($elements[$i], '=')) {
+ $elName = trim($elements[$i]);
+ $elValue = null;
+ } else {
+ list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+ }
+ $elName = strtolower($elName);
+ if ('secure' == $elName) {
+ $cookie['secure'] = true;
+ } elseif ('expires' == $elName) {
+ $cookie['expires'] = str_replace('"', '', $elValue);
+ } elseif ('path' == $elName || 'domain' == $elName) {
+ $cookie[$elName] = urldecode($elValue);
+ } else {
+ $cookie[$elName] = $elValue;
+ }
+ }
+ }
+ $this->_cookies[] = $cookie;
+ }
+
+
+ /**
+ * Read a part of response body encoded with chunked Transfer-Encoding
+ *
+ * @access private
+ * @return string
+ */
+ function _readChunked()
+ {
+ // at start of the next chunk?
+ if (0 == $this->_chunkLength) {
+ $line = $this->_sock->readLine();
+ if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
+ $this->_chunkLength = hexdec($matches[1]);
+ // Chunk with zero length indicates the end
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // make this an eof()
+ return '';
+ }
+ } else {
+ return '';
+ }
+ }
+ $data = $this->_sock->read($this->_chunkLength);
+ $this->_chunkLength -= strlen($data);
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // Trailing CRLF
+ }
+ return $data;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ * @see HTTP_Request::_notify()
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+} // End class HTTP_Response
+?>
diff --git a/thirdparty/pear/WebDAV/Request/Listener.php b/thirdparty/pear/WebDAV/Request/Listener.php
new file mode 100644
index 0000000..56a8342
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Request/Listener.php
@@ -0,0 +1,96 @@
+ |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Listener.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+//
+
+/**
+ * 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.
+ *
+ * @package HTTP_Request
+ * @author Alexey Borzov
+ * @version $Revision: 6819 $
+ */
+class HTTP_Request_Listener
+{
+ /**
+ * A listener's identifier
+ * @var string
+ */
+ var $_id;
+
+ /**
+ * Constructor, sets the object's identifier
+ *
+ * @access public
+ */
+ function HTTP_Request_Listener()
+ {
+ $this->_id = md5(uniqid('http_request_', 1));
+ }
+
+
+ /**
+ * Returns the listener's identifier
+ *
+ * @access public
+ * @return string
+ */
+ function getId()
+ {
+ return $this->_id;
+ }
+
+
+ /**
+ * This method is called when Listener is notified of an event
+ *
+ * @access public
+ * @param object an object the listener is attached to
+ * @param string Event name
+ * @param mixed Additional data
+ * @abstract
+ */
+ function update(&$subject, $event, $data = null)
+ {
+ echo "Notified of event: '$event'\n";
+ if (null !== $data) {
+ echo "Additional data: ";
+ var_dump($data);
+ }
+ }
+}
+?>
diff --git a/thirdparty/pear/WebDAV/Upload.php b/thirdparty/pear/WebDAV/Upload.php
new file mode 100644
index 0000000..ca29eb6
--- /dev/null
+++ b/thirdparty/pear/WebDAV/Upload.php
@@ -0,0 +1,856 @@
+ http://www.gnu.org/copyleft/lesser.txt
+// (c) 2001-2004 by Tomas Von Veschler Cox
+//
+// **********************************************
+//
+// $Id: Upload.php 6819 2007-06-20 13:09:21Z kevin_fourie $
+
+/*
+ * Pear File Uploader class. Easy and secure managment of files
+ * submitted via HTML Forms.
+ *
+ * Leyend:
+ * - you can add error msgs in your language in the HTTP_Upload_Error class
+ *
+ * TODO:
+ * - try to think a way of having all the Error system in other
+ * file and only include it when an error ocurrs
+ *
+ * -- Notes for users HTTP_Upload >= 0.9.0 --
+ *
+ * Error detection was enhanced, so you no longer need to
+ * check for PEAR::isError() in $upload->getFiles() or call
+ * $upload->isMissing(). Instead you'll
+ * get the error when do a check for $file->isError().
+ *
+ * Example:
+ *
+ * $upload = new HTTP_Upload('en');
+ * $file = $upload->getFiles('i_dont_exist_in_form_definition');
+ * if ($file->isError()) {
+ * die($file->getMessage());
+ * }
+ *
+ * --
+ *
+ */
+
+require_once 'PEAR.php';
+
+/**
+ * defines default chmod
+ */
+define('HTTP_UPLOAD_DEFAULT_CHMOD', 0660);
+
+/**
+ * Error Class for HTTP_Upload
+ *
+ * @author Tomas V.V.Cox
+ * @see http://vulcanonet.com/soft/index.php?pack=uploader
+ * @package HTTP_Upload
+ * @category HTTP
+ * @access public
+ */
+class HTTP_Upload_Error extends PEAR
+{
+ /**
+ * Selected language for error messages
+ * @var string
+ */
+ var $lang = 'en';
+
+ /**
+ * Whether HTML entities shall be encoded automatically
+ * @var boolean
+ */
+ var $html = false;
+
+ /**
+ * Constructor
+ *
+ * Creates a new PEAR_Error
+ *
+ * @param string $lang The language selected for error code messages
+ * @access public
+ */
+ function HTTP_Upload_Error($lang = null, $html = false)
+ {
+ $this->lang = ($lang !== null) ? $lang : $this->lang;
+ $this->html = ($html !== false) ? $html : $this->html;
+ $ini_size = preg_replace('/m/i', '000000', ini_get('upload_max_filesize'));
+
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.1', 'ge')) {
+ $maxsize = (isset($_POST['MAX_FILE_SIZE'])) ?
+ $_POST['MAX_FILE_SIZE'] : null;
+ } else {
+ global $HTTP_POST_VARS;
+ $maxsize = (isset($HTTP_POST_VARS['MAX_FILE_SIZE'])) ?
+ $HTTP_POST_VARS['MAX_FILE_SIZE'] : null;
+ }
+
+ if (empty($maxsize) || ($maxsize > $ini_size)) {
+ $maxsize = $ini_size;
+ }
+ // XXXXX Add here error messages in your language
+ $this->error_codes = array(
+ 'TOO_LARGE' => array(
+ 'es' => "Fichero demasiado largo. El maximo permitido es: $maxsize bytes.",
+ 'en' => "File size too large. The maximum permitted size is: $maxsize bytes.",
+ 'de' => "Datei zu groß. Die zulässige Maximalgröße ist: $maxsize Bytes.",
+ 'nl' => "Het bestand is te groot, de maximale grootte is: $maxsize bytes.",
+ 'fr' => "Le fichier est trop gros. La taille maximum autorisée est: $maxsize bytes.",
+ 'it' => "Il file é troppo grande. Il massimo permesso é: $maxsize bytes.",
+ 'pt_BR' => "Arquivo muito grande. O tamanho máximo permitido é $maxsize bytes."
+ ),
+ 'MISSING_DIR' => array(
+ 'es' => 'Falta directorio destino.',
+ 'en' => 'Missing destination directory.',
+ 'de' => 'Kein Zielverzeichnis definiert.',
+ 'nl' => 'Geen bestemmings directory.',
+ 'fr' => 'Le répertoire de destination n\'est pas défini.',
+ 'it' => 'Manca la directory di destinazione.',
+ 'pt_BR' => 'Ausência de diretório de destino.'
+ ),
+ 'IS_NOT_DIR' => array(
+ 'es' => 'El directorio destino no existe o es un fichero regular.',
+ 'en' => 'The destination directory doesn\'t exist or is a regular file.',
+ 'de' => 'Das angebene Zielverzeichnis existiert nicht oder ist eine Datei.',
+ 'nl' => 'De doeldirectory bestaat niet, of is een gewoon bestand.',
+ 'fr' => 'Le répertoire de destination n\'existe pas ou il s\'agit d\'un fichier régulier.',
+ 'it' => 'La directory di destinazione non esiste o é un file.',
+ 'pt_BR' => 'O diretório de destino não existe ou é um arquivo.'
+ ),
+ 'NO_WRITE_PERMS' => array(
+ 'es' => 'El directorio destino no tiene permisos de escritura.',
+ 'en' => 'The destination directory doesn\'t have write perms.',
+ 'de' => 'Fehlende Schreibrechte für das Zielverzeichnis.',
+ 'nl' => 'Geen toestemming om te schrijven in de doeldirectory.',
+ 'fr' => 'Le répertoire de destination n\'a pas les droits en écriture.',
+ 'it' => 'Non si hanno i permessi di scrittura sulla directory di destinazione.',
+ 'pt_BR' => 'O diretório de destino não possui permissão para escrita.'
+ ),
+ 'NO_USER_FILE' => array(
+ 'es' => 'No se ha escogido fichero para el upload.',
+ 'en' => 'You haven\'t selected any file for uploading.',
+ 'de' => 'Es wurde keine Datei für den Upload ausgewählt.',
+ 'nl' => 'Er is geen bestand opgegeven om te uploaden.',
+ 'fr' => 'Vous n\'avez pas sélectionné de fichier à envoyer.',
+ 'it' => 'Nessun file selezionato per l\'upload.',
+ 'pt_BR' => 'Nenhum arquivo selecionado para upload.'
+ ),
+ 'BAD_FORM' => array(
+ 'es' => 'El formulario no contiene method="post" enctype="multipart/form-data" requerido.',
+ 'en' => 'The html form doesn\'t contain the required method="post" enctype="multipart/form-data".',
+ 'de' => 'Das HTML-Formular enthält nicht die Angabe method="post" enctype="multipart/form-data" '.
+ 'im >form<-Tag.',
+ 'nl' => 'Het HTML-formulier bevat niet de volgende benodigde '.
+ 'eigenschappen: method="post" enctype="multipart/form-data".',
+ 'fr' => 'Le formulaire HTML ne contient pas les attributs requis : '.
+ ' method="post" enctype="multipart/form-data".',
+ 'it' => 'Il modulo HTML non contiene gli attributi richiesti: "'.
+ ' method="post" enctype="multipart/form-data".',
+ 'pt_BR' => 'O formulário HTML não possui o method="post" enctype="multipart/form-data" requerido.'
+ ),
+ 'E_FAIL_COPY' => array(
+ 'es' => 'Fallo al copiar el fichero temporal.',
+ 'en' => 'Failed to copy the temporary file.',
+ 'de' => 'Temporäre Datei konnte nicht kopiert werden.',
+ 'nl' => 'Het tijdelijke bestand kon niet gekopieerd worden.',
+ 'fr' => 'L\'enregistrement du fichier temporaire a échoué.',
+ 'it' => 'Copia del file temporaneo fallita.',
+ 'pt_BR' => 'Falha ao copiar o arquivo temporário.'
+ ),
+ 'E_FAIL_MOVE' => array(
+ 'es' => 'No puedo mover el fichero.',
+ 'en' => 'Impossible to move the file.',
+ 'de' => 'Datei kann nicht verschoben werden.',
+ 'nl' => 'Het bestand kon niet verplaatst worden.',
+ 'fr' => 'Impossible de déplacer le fichier.',
+ 'pt_BR' => 'Não foi possível mover o arquivo.'
+ ),
+ 'FILE_EXISTS' => array(
+ 'es' => 'El fichero destino ya existe.',
+ 'en' => 'The destination file already exists.',
+ 'de' => 'Die zu erzeugende Datei existiert bereits.',
+ 'nl' => 'Het doelbestand bestaat al.',
+ 'fr' => 'Le fichier de destination existe déjà.',
+ 'it' => 'File destinazione già esistente.',
+ 'pt_BR' => 'O arquivo de destino já existe.'
+ ),
+ 'CANNOT_OVERWRITE' => array(
+ 'es' => 'El fichero destino ya existe y no se puede sobreescribir.',
+ 'en' => 'The destination file already exists and could not be overwritten.',
+ 'de' => 'Die zu erzeugende Datei existiert bereits und konnte nicht überschrieben werden.',
+ 'nl' => 'Het doelbestand bestaat al, en kon niet worden overschreven.',
+ 'fr' => 'Le fichier de destination existe déjà et ne peux pas être remplacé.',
+ 'it' => 'File destinazione già esistente e non si può sovrascrivere.',
+ 'pt_BR' => 'O arquivo de destino já existe e não pôde ser sobrescrito.'
+ ),
+ 'NOT_ALLOWED_EXTENSION' => array(
+ 'es' => 'Extension de fichero no permitida.',
+ 'en' => 'File extension not permitted.',
+ 'de' => 'Unerlaubte Dateiendung.',
+ 'nl' => 'Niet toegestane bestands-extensie.',
+ 'fr' => 'Le fichier a une extension non autorisée.',
+ 'it' => 'Estensione del File non permessa.',
+ 'pt_BR' => 'Extensão de arquivo não permitida.'
+ ),
+ 'PARTIAL' => array(
+ 'es' => 'El fichero fue parcialmente subido',
+ 'en' => 'The file was only partially uploaded.',
+ 'de' => 'Die Datei wurde unvollständig übertragen.',
+ 'nl' => 'Het bestand is slechts gedeeltelijk geupload.',
+ 'pt_BR' => 'O arquivo não foi enviado por completo.'
+ ),
+ 'ERROR' => array(
+ 'es' => 'Error en subida:',
+ 'en' => 'Upload error:',
+ 'de' => 'Fehler beim Upload:',
+ 'nl' => 'Upload fout:',
+ 'pt_BR' => 'Erro de upload:'
+ ),
+ 'DEV_NO_DEF_FILE' => array(
+ 'es' => 'No está definido en el formulario este nombre de fichero como <input type="file" name=?>.',
+ 'en' => 'This filename is not defined in the form as <input type="file" name=?>.',
+ 'de' => 'Dieser Dateiname ist im Formular nicht als <input type="file" name=?> definiert.',
+ 'nl' => 'Deze bestandsnaam is niett gedefineerd in het formulier als <input type="file" name=?>.'
+ )
+ );
+ }
+
+ /**
+ * returns the error code
+ *
+ * @param string $e_code type of error
+ * @return string Error message
+ */
+ function errorCode($e_code)
+ {
+ if (!empty($this->error_codes[$e_code][$this->lang])) {
+ $msg = $this->html ?
+ html_entity_decode($this->error_codes[$e_code][$this->lang]) :
+ $this->error_codes[$e_code][$this->lang];
+ } else {
+ $msg = $e_code;
+ }
+
+ if (!empty($this->error_codes['ERROR'][$this->lang])) {
+ $error = $this->error_codes['ERROR'][$this->lang];
+ } else {
+ $error = $this->error_codes['ERROR']['en'];
+ }
+ return $error.' '.$msg;
+ }
+
+ /**
+ * Overwrites the PEAR::raiseError method
+ *
+ * @param string $e_code type of error
+ * @return object PEAR_Error a PEAR-Error object
+ * @access public
+ */
+ function raiseError($e_code)
+ {
+ return PEAR::raiseError($this->errorCode($e_code), $e_code);
+ }
+}
+
+/**
+ * This class provides an advanced file uploader system
+ * for file uploads made from html forms
+
+ *
+ * @author Tomas V.V.Cox
+ * @see http://vulcanonet.com/soft/index.php?pack=uploader
+ * @package HTTP_Upload
+ * @category HTTP
+ * @access public
+ */
+class HTTP_Upload extends HTTP_Upload_Error
+{
+ /**
+ * Contains an array of "uploaded files" objects
+ * @var array
+ */
+ var $files = array();
+
+ /**
+ * Contains the desired chmod for uploaded files
+ * @var int
+ * @access private
+ */
+ var $_chmod = HTTP_UPLOAD_DEFAULT_CHMOD;
+
+ /**
+ * Constructor
+ *
+ * @param string $lang Language to use for reporting errors
+ * @see Upload_Error::error_codes
+ * @access public
+ */
+ function HTTP_Upload($lang = null)
+ {
+ $this->HTTP_Upload_Error($lang);
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.1', 'ge'))
+ {
+ $this->post_files = $_FILES;
+ if (isset($_SERVER['CONTENT_TYPE'])) {
+ $this->content_type = $_SERVER['CONTENT_TYPE'];
+ }
+ } else {
+ global $HTTP_POST_FILES, $HTTP_SERVER_VARS;
+ $this->post_files = $HTTP_POST_FILES;
+ if (isset($HTTP_SERVER_VARS['CONTENT_TYPE'])) {
+ $this->content_type = $HTTP_SERVER_VARS['CONTENT_TYPE'];
+ }
+ }
+ }
+
+ /**
+ * Get files
+ *
+ * @param mixed $file If:
+ * - not given, function will return array of upload_file objects
+ * - is int, will return the $file position in upload_file objects array
+ * - is string, will return the upload_file object corresponding
+ * to $file name of the form. For ex:
+ * if form is
+ * to get this file use: $upload->getFiles('userfile')
+ *
+ * @return mixed array or object (see @param $file above) or Pear_Error
+ * @access public
+ */
+ function &getFiles($file = null)
+ {
+ static $is_built = false;
+ //build only once for multiple calls
+ if (!$is_built) {
+ $files = &$this->_buildFiles();
+ if (PEAR::isError($files)) {
+ // there was an error with the form.
+ // Create a faked upload embedding the error
+ $this->files['_error'] = &new HTTP_Upload_File(
+ '_error', null,
+ null, null,
+ null, $files->getCode(),
+ $this->lang, $this->_chmod);
+ } else {
+ $this->files = $files;
+ }
+ $is_built = true;
+ }
+ if ($file !== null) {
+ if (is_int($file)) {
+ $pos = 0;
+ foreach ($this->files as $obj) {
+ if ($pos == $file) {
+ return $obj;
+ }
+ $pos++;
+ }
+ } elseif (is_string($file) && isset($this->files[$file])) {
+ return $this->files[$file];
+ }
+ if (isset($this->files['_error'])) {
+ return $this->files['_error'];
+ } else {
+ // developer didn't specify this name in the form
+ // warn him about it with a faked upload
+ return new HTTP_Upload_File(
+ '_error', null,
+ null, null,
+ null, 'DEV_NO_DEF_FILE',
+ $this->lang);
+ }
+ }
+ return $this->files;
+ }
+
+ /**
+ * Creates the list of the uploaded file
+ *
+ * @return array of HTTP_Upload_File objects for every file
+ */
+ function &_buildFiles()
+ {
+ // Form method check
+ if (!isset($this->content_type) ||
+ strpos($this->content_type, 'multipart/form-data') !== 0)
+ {
+ return $this->raiseError('BAD_FORM');
+ }
+ // In 4.1 $_FILES isn't initialized when no uploads
+ // XXX (cox) afaik, in >= 4.1 and <= 4.3 only
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.1', 'ge'))
+ {
+ $error = $this->isMissing();
+ if (PEAR::isError($error)) {
+ return $error;
+ }
+ }
+
+ // map error codes from 4.2.0 $_FILES['userfile']['error']
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.2.0', 'ge')) {
+ $uploadError = array(
+ 1 => 'TOO_LARGE',
+ 2 => 'TOO_LARGE',
+ 3 => 'PARTIAL',
+ 4 => 'NO_USER_FILE'
+ );
+ }
+
+
+ // Parse $_FILES (or $HTTP_POST_FILES)
+ $files = array();
+ foreach ($this->post_files as $userfile => $value) {
+ if (is_array($value['name'])) {
+ foreach ($value['name'] as $key => $val) {
+ $err = $value['error'][$key];
+ if (isset($err) && $err !== 0 && isset($uploadError[$err])) {
+ $error = $uploadError[$err];
+ } else {
+ $error = null;
+ }
+ $name = basename($value['name'][$key]);
+ $tmp_name = $value['tmp_name'][$key];
+ $size = $value['size'][$key];
+ $type = $value['type'][$key];
+ $formname = $userfile . "[$key]";
+ $files[$formname] = new HTTP_Upload_File($name, $tmp_name,
+ $formname, $type, $size, $error, $this->lang, $this->_chmod);
+ }
+ // One file
+ } else {
+ $err = $value['error'];
+ if (isset($err) && $err !== 0 && isset($uploadError[$err])) {
+ $error = $uploadError[$err];
+ } else {
+ $error = null;
+ }
+ $name = basename($value['name']);
+ $tmp_name = $value['tmp_name'];
+ $size = $value['size'];
+ $type = $value['type'];
+ $formname = $userfile;
+ $files[$formname] = new HTTP_Upload_File($name, $tmp_name,
+ $formname, $type, $size, $error, $this->lang, $this->_chmod);
+ }
+ }
+ return $files;
+ }
+
+ /**
+ * Checks if the user submited or not some file
+ *
+ * @return mixed False when are files or PEAR_Error when no files
+ * @access public
+ * @see Read the note in the source code about this function
+ */
+ function isMissing()
+ {
+ if (count($this->post_files) < 1) {
+ return $this->raiseError('NO_USER_FILE');
+ }
+ //we also check if at least one file has more than 0 bytes :)
+ $files = array();
+ $size = 0;
+ foreach ($this->post_files as $userfile => $value) {
+ if (is_array($value['name'])) {
+ foreach ($value['name'] as $key => $val) {
+ $size += $value['size'][$key];
+ }
+ } else { //one file
+ $size = $value['size'];
+ }
+ }
+ if ($size == 0) {
+ $this->raiseError('NO_USER_FILE');
+ }
+ return false;
+ }
+
+ /**
+ * Sets the chmod to be used for uploaded files
+ *
+ * @param int Desired mode
+ */
+ function setChmod($mode)
+ {
+ $this->_chmod = $mode;
+ }
+}
+
+/**
+ * This class provides functions to work with the uploaded file
+ *
+ * @author Tomas V.V.Cox
+ * @see http://vulcanonet.com/soft/index.php?pack=uploader
+ * @package HTTP_Upload
+ * @category HTTP
+ * @access public
+ */
+class HTTP_Upload_File extends HTTP_Upload_Error
+{
+ /**
+ * If the random seed was initialized before or not
+ * @var boolean;
+ */
+ var $_seeded = 0;
+
+ /**
+ * Assoc array with file properties
+ * @var array
+ */
+ var $upload = array();
+
+ /**
+ * If user haven't selected a mode, by default 'safe' will be used
+ * @var boolean
+ */
+ var $mode_name_selected = false;
+
+ /**
+ * It's a common security risk in pages who has the upload dir
+ * under the document root (remember the hack of the Apache web?)
+ *
+ * @var array
+ * @access private
+ * @see HTTP_Upload_File::setValidExtensions()
+ */
+ var $_extensions_check = array('php', 'phtm', 'phtml', 'php3', 'inc');
+
+ /**
+ * @see HTTP_Upload_File::setValidExtensions()
+ * @var string
+ * @access private
+ */
+ var $_extensions_mode = 'deny';
+
+ /**
+ * Contains the desired chmod for uploaded files
+ * @var int
+ * @access private
+ */
+ var $_chmod = HTTP_UPLOAD_DEFAULT_CHMOD;
+
+ /**
+ * Constructor
+ *
+ * @param string $name destination file name
+ * @param string $tmp temp file name
+ * @param string $formname name of the form
+ * @param string $type Mime type of the file
+ * @param string $size size of the file
+ * @param string $error error on upload
+ * @param string $lang used language for errormessages
+ * @access public
+ */
+ function HTTP_Upload_File($name = null, $tmp = null, $formname = null,
+ $type = null, $size = null, $error = null,
+ $lang = null, $chmod = HTTP_UPLOAD_DEFAULT_CHMOD)
+ {
+ $this->HTTP_Upload_Error($lang);
+ $ext = null;
+
+ if (empty($name) || $size == 0) {
+ $error = 'NO_USER_FILE';
+ } elseif ($tmp == 'none') {
+ $error = 'TOO_LARGE';
+ } else {
+ // strpos needed to detect files without extension
+ if (($pos = strrpos($name, '.')) !== false) {
+ $ext = substr($name, $pos + 1);
+ }
+ }
+
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.1', 'ge')) {
+ if (isset($_POST['MAX_FILE_SIZE']) &&
+ $size > $_POST['MAX_FILE_SIZE']) {
+ $error = 'TOO_LARGE';
+ }
+ } else {
+ global $HTTP_POST_VARS;
+ if (isset($HTTP_POST_VARS['MAX_FILE_SIZE']) &&
+ $size > $HTTP_POST_VARS['MAX_FILE_SIZE']) {
+ $error = 'TOO_LARGE';
+ }
+ }
+
+ $this->upload = array(
+ 'real' => $name,
+ 'name' => $name,
+ 'form_name' => $formname,
+ 'ext' => $ext,
+ 'tmp_name' => $tmp,
+ 'size' => $size,
+ 'type' => $type,
+ 'error' => $error
+ );
+
+ $this->_chmod = $chmod;
+ }
+
+ /**
+ * Sets the name of the destination file
+ *
+ * @param string $mode A valid mode: 'uniq', 'safe' or 'real' or a file name
+ * @param string $prepend A string to prepend to the name
+ * @param string $append A string to append to the name
+ *
+ * @return string The modified name of the destination file
+ * @access public
+ */
+ function setName($mode, $prepend = null, $append = null)
+ {
+ switch ($mode) {
+ case 'uniq':
+ $name = $this->nameToUniq();
+ $this->upload['ext'] = $this->nameToSafe($this->upload['ext'], 10);
+ $name .= '.' . $this->upload['ext'];
+ break;
+ case 'safe':
+ $name = $this->nameToSafe($this->upload['real']);
+ if (($pos = strrpos($name, '.')) !== false) {
+ $this->upload['ext'] = substr($name, $pos + 1);
+ } else {
+ $this->upload['ext'] = '';
+ }
+ break;
+ case 'real':
+ $name = $this->upload['real'];
+ break;
+ default:
+ $name = $mode;
+ }
+ $this->upload['name'] = $prepend . $name . $append;
+ $this->mode_name_selected = true;
+ return $this->upload['name'];
+ }
+
+ /**
+ * Unique file names in the form: 9022210413b75410c28bef.html
+ * @see HTTP_Upload_File::setName()
+ */
+ function nameToUniq()
+ {
+ if (! $this->_seeded) {
+ srand((double) microtime() * 1000000);
+ $this->_seeded = 1;
+ }
+ $uniq = uniqid(rand());
+ return $uniq;
+ }
+
+ /**
+ * Format a file name to be safe
+ *
+ * @param string $file The string file name
+ * @param int $maxlen Maximun permited string lenght
+ * @return string Formatted file name
+ * @see HTTP_Upload_File::setName()
+ */
+ function nameToSafe($name, $maxlen=250)
+ {
+ $noalpha = 'ÁÉÍÓÚÝáéíóúýÂÊÎÔÛâêîôûÀÈÌÒÙàèìòùÄËÏÖÜäëïöüÿÃãÕõÅåÑñÇç@°ºª';
+ $alpha = 'AEIOUYaeiouyAEIOUaeiouAEIOUaeiouAEIOUaeiouyAaOoAaNnCcaooa';
+
+ $name = substr($name, 0, $maxlen);
+ $name = strtr($name, $noalpha, $alpha);
+ // not permitted chars are replaced with "_"
+ return preg_replace('/[^a-zA-Z0-9,._\+\()\-]/', '_', $name);
+ }
+
+ /**
+ * The upload was valid
+ *
+ * @return bool If the file was submitted correctly
+ * @access public
+ */
+ function isValid()
+ {
+ if ($this->upload['error'] === null) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * User haven't submit a file
+ *
+ * @return bool If the user submitted a file or not
+ * @access public
+ */
+ function isMissing()
+ {
+ if ($this->upload['error'] == 'NO_USER_FILE') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Some error occured during upload (most common due a file size problem,
+ * like max size exceeded or 0 bytes long).
+ * @return bool If there were errors submitting the file (probably
+ * because the file excess the max permitted file size)
+ * @access public
+ */
+ function isError()
+ {
+ if (in_array($this->upload['error'], array('TOO_LARGE', 'BAD_FORM','DEV_NO_DEF_FILE'))) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Moves the uploaded file to its destination directory.
+ *
+ * @param string $dir_dest Destination directory
+ * @param bool $overwrite Overwrite if destination file exists?
+ * @return mixed True on success or Pear_Error object on error
+ * @access public
+ */
+ function moveTo($dir_dest, $overwrite = true)
+ {
+ if (!$this->isValid()) {
+ return $this->raiseError($this->upload['error']);
+ }
+
+ //Valid extensions check
+ if (!$this->_evalValidExtensions()) {
+ return $this->raiseError('NOT_ALLOWED_EXTENSION');
+ }
+
+ $err_code = $this->_chk_dir_dest($dir_dest);
+ if ($err_code !== false) {
+ return $this->raiseError($err_code);
+ }
+ // Use 'safe' mode by default if no other was selected
+ if (!$this->mode_name_selected) {
+ $this->setName('safe');
+ }
+
+ $name_dest = $dir_dest . DIRECTORY_SEPARATOR . $this->upload['name'];
+
+ if (@is_file($name_dest)) {
+ if ($overwrite !== true) {
+ return $this->raiseError('FILE_EXISTS');
+ } elseif (!is_writable($name_dest)) {
+ return $this->raiseError('CANNOT_OVERWRITE');
+ }
+ }
+
+ // copy the file and let php clean the tmp
+ if (!@move_uploaded_file($this->upload['tmp_name'], $name_dest)) {
+ return $this->raiseError('E_FAIL_MOVE');
+ }
+ @chmod($name_dest, $this->_chmod);
+ return $this->getProp('name');
+ }
+
+ /**
+ * Check for a valid destination dir
+ *
+ * @param string $dir_dest Destination dir
+ * @return mixed False on no errors or error code on error
+ */
+ function _chk_dir_dest($dir_dest)
+ {
+ if (!$dir_dest) {
+ return 'MISSING_DIR';
+ }
+ if (!@is_dir ($dir_dest)) {
+ return 'IS_NOT_DIR';
+ }
+ if (!is_writeable ($dir_dest)) {
+ return 'NO_WRITE_PERMS';
+ }
+ return false;
+ }
+ /**
+ * Retrive properties of the uploaded file
+ * @param string $name The property name. When null an assoc array with
+ * all the properties will be returned
+ * @return mixed A string or array
+ * @see HTTP_Upload_File::HTTP_Upload_File()
+ * @access public
+ */
+ function getProp($name = null)
+ {
+ if ($name === null) {
+ return $this->upload;
+ }
+ return $this->upload[$name];
+ }
+
+ /**
+ * Returns a error message, if a error occured
+ * (deprecated) Use getMessage() instead
+ * @return string a Error message
+ * @access public
+ */
+ function errorMsg()
+ {
+ return $this->errorCode($this->upload['error']);
+ }
+
+ /**
+ * Returns a error message, if a error occured
+ * @return string a Error message
+ * @access public
+ */
+ function getMessage()
+ {
+ return $this->errorCode($this->upload['error']);
+ }
+
+ /**
+ * Function to restrict the valid extensions on file uploads
+ *
+ * @param array $exts File extensions to validate
+ * @param string $mode The type of validation:
+ * 1) 'deny' Will deny only the supplied extensions
+ * 2) 'accept' Will accept only the supplied extensions
+ * as valid
+ * @access public
+ */
+ function setValidExtensions($exts, $mode = 'deny')
+ {
+ $this->_extensions_check = $exts;
+ $this->_extensions_mode = $mode;
+ }
+
+ /**
+ * Evaluates the validity of the extensions set by setValidExtensions
+ *
+ * @return bool False on non valid extension, true if they are valid
+ * @access private
+ */
+ function _evalValidExtensions()
+ {
+ $exts = $this->_extensions_check;
+ settype($exts, 'array');
+ if ($this->_extensions_mode == 'deny') {
+ if (in_array($this->getProp('ext'), $exts)) {
+ return false;
+ }
+ // mode == 'accept'
+ } else {
+ if (!in_array($this->getProp('ext'), $exts)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+?>
\ No newline at end of file