Commit 653c54f28cdd7142a0826314612f8000db8eb52d

Authored by kevin_fourie
1 parent ce3aef23

Merged in from DEV trunk...

KTS-2307
""
Fixed. The error message has been changed. 

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-2453
""
Updated. Wording changed.

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-2497
"Creation of a new thread fails with: 'DB error: unknown error'"
Fixed.

Committed By: Conrad Vermeulen
Reviewed By: Megan Watson

BBS-458
"Update WebDAV PEAR class"
Fixed. Moved webdav class into the ktwebdav directory to avoid conflicts with the Baobab modified webdav class. Upgraded the ktwebdav to version 1.0.0RC4(beta).

Committed by: Megan Watson
Reviewed by: Conrad Vermeulen

KTS-2644
"Add collision detection to document adding"
Fixed.

Committed By: Jalaloedien Abrahams
Reviewed By: Conrad Vermeulen

KTS-447
"Validation improvements needed when adding a user or group"
Fixed. Added a preg match function to check for special characters

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-447
"Validation improvements needed when adding a user or group"
Fixed. Added the preg match function to check for special characters. 

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTC-246
"Footer Text update"
Updated. New image in use.

Committed By: Kevin Fourie
Reviewed By: Jalaloedien Abrahams

KTS-1710
"Need to ensure US English spelling on text throughout the system"
Fixed. Wording changed.

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-2624
"Linux: FedoraCore5: Attempting to use the bulk action 'Checkout' on folders results in an empty zip being created."
Fixed. Changed the check on checkedout documents to add an error message and not return out of the function.

Committed by: Megan Watson
Reviewed by: Conrad Vermeulen

KTS-1710
"Need to ensure US English spelling on text throughout the system"
Fixed.Wording changed.

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-1710
"Need to ensure US English spelling on text throughout the system"
Fixed. Wording changed.

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-1710
"Need to ensure US English spelling on text throughout the system"
Fixed. Wording changed

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-1710
"Need to ensure US English spelling on text throughout the system"
Fixed. Wording changed.

Committed By:Yusuf Davids
Reviewed By:Jalaloedien Abrahams

KTS-2652
"Would be nice to have a dedicated rss feed dashlet for KnowledgeTree blog, etc"
Fixed. Extended rss plugin

Committed By: Jalaloedien Abrahams
Reviewed By: Conrad Vermeulen

KTS-2654
"Bulk export doesn't include empty folders in the base folder being exported"
Fixed. Added current folder to the zip file.

Committed by: Megan Watson
Reviewed by: Conrad Vermeulen


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/STABLE/trunk@7628 c91229c3-7414-0410-bfa2-8a42b809f60b
HISTORY.txt
... ... @@ -58,7 +58,7 @@ Improvements
58 58 New Features
59 59  
60 60 * Search queries saveable on per user-basis
61   - * The login page can now be customised through-the-web.
  61 + * The login page can now be customized through-the-web.
62 62 * Admin can now disable the "preferences" dialog for all users.
63 63 * Ensuring that your system is "transaction safe" can now be done
64 64 through the web interface.
... ...
config/config.ini
... ... @@ -195,6 +195,10 @@ tmpDirectory = ${varDirectory}/tmp
195 195 graphicsUrl = ${rootUrl}/graphics
196 196 uiUrl = ${rootUrl}/presentation/lookAndFeel/knowledgeTree
197 197  
  198 +;dedicated feed url
  199 +;dedicatedrsstitle = RSS Feed Title
  200 +;dedicatedrssurl =
  201 +
198 202 ; files
199 203 stopwordsFile = ${fileSystemRoot}/config/stopwords.txt
200 204  
... ...
i18n/knowledgeTree.pot
... ... @@ -3474,7 +3474,7 @@ msgid "Existing Searches"
3474 3474 msgstr ""
3475 3475  
3476 3476 #: i18n/templates.c:5507
3477   -msgid "Existing customised help pages"
  3477 +msgid "Existing customized help pages"
3478 3478 msgstr ""
3479 3479  
3480 3480 #: i18n/templates.c:785
... ... @@ -5622,7 +5622,7 @@ msgid "No groups or roles are defined in the DMS."
5622 5622 msgstr ""
5623 5623  
5624 5624 #: i18n/templates.c:5519
5625   -msgid "No help files have been customised."
  5625 +msgid "No help files have been customized."
5626 5626 msgstr ""
5627 5627  
5628 5628 #: help.php:83 help.php:84
... ... @@ -6405,7 +6405,7 @@ msgid "Please provide a search term"
6405 6405 msgstr ""
6406 6406  
6407 6407 #: i18n/templates.c:782
6408   -msgid "Please select a disclaimer to customise."
  6408 +msgid "Please select a disclaimer to customize."
6409 6409 msgstr ""
6410 6410  
6411 6411 #: lib/authentication/builtinauthenticationprovider.inc.php:64
... ... @@ -8427,7 +8427,7 @@ msgid "To connect with the #appname# Tools for Windows package, use this address
8427 8427 msgstr ""
8428 8428  
8429 8429 #: i18n/templates.c:5504
8430   -msgid "To customise a help file, please visit that file via the help system and click on <strong>customise this help file</strong>."
  8430 +msgid "To customize a help file, please visit that file via the help system and click on <strong>customize this help file</strong>."
8431 8431 msgstr ""
8432 8432  
8433 8433 #: i18n/templates.c:1715
... ...
i18n/templates.c
... ... @@ -779,7 +779,7 @@ gettext(&quot;Update&quot;);
779 779 gettext("Disclaimers");
780 780  
781 781 /* ./templates/ktstandard/disclaimers/manage_disclaimers.smarty */
782   -gettext("Please select a disclaimer to customise.");
  782 +gettext("Please select a disclaimer to customize.");
783 783  
784 784 /* ./templates/ktstandard/disclaimers/manage_disclaimers.smarty */
785 785 gettext("Existing disclaimers");
... ... @@ -5510,10 +5510,10 @@ gettext(&quot;Create Fieldset&quot;);
5510 5510 gettext("Current help assignments");
5511 5511  
5512 5512 /* ./templates/ktcore/manage_help.smarty */
5513   -gettext("To customise a help file, please visit that file via the help system and click on <strong>customise this help file</strong>.");
  5513 +gettext("To customize a help file, please visit that file via the help system and click on <strong>customize this help file</strong>.");
5514 5514  
5515 5515 /* ./templates/ktcore/manage_help.smarty */
5516   -gettext("Existing customised help pages");
  5516 +gettext("Existing customized help pages");
5517 5517  
5518 5518 /* ./templates/ktcore/manage_help.smarty */
5519 5519 gettext("Name");
... ... @@ -5525,7 +5525,7 @@ gettext(&quot;Actions&quot;);
5525 5525 gettext("Delete");
5526 5526  
5527 5527 /* ./templates/ktcore/manage_help.smarty */
5528   -gettext("No help files have been customised.");
  5528 +gettext("No help files have been customized.");
5529 5529  
5530 5530 /* ./templates/ktcore/boolean_search_change.smarty */
5531 5531 gettext("Boolean Search");
... ...
ktwebdav/ktwebdav.php
... ... @@ -6,39 +6,40 @@
6 6 * KnowledgeTree Open Source Edition
7 7 * Document Management Made Simple
8 8 * Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
9   - *
  9 + *
10 10 * This program is free software; you can redistribute it and/or modify it under
11 11 * the terms of the GNU General Public License version 3 as published by the
12 12 * Free Software Foundation.
13   - *
  13 + *
14 14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 17 * details.
18   - *
  18 + *
19 19 * You should have received a copy of the GNU General Public License
20 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21   - *
  21 + *
22 22 * You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
23 23 * Blake Street, Observatory, 7925 South Africa. or email info@knowledgetree.com.
24   - *
  24 + *
25 25 * The interactive user interfaces in modified source and object code versions
26 26 * of this program must display Appropriate Legal Notices, as required under
27 27 * Section 5 of the GNU General Public License version 3.
28   - *
  28 + *
29 29 * In accordance with Section 7(b) of the GNU General Public License version 3,
30 30 * these Appropriate Legal Notices must retain the display of the "Powered by
31   - * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  31 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
32 32 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
33   - * must display the words "Powered by KnowledgeTree" and retain the original
34   - * copyright notice.
  33 + * must display the words "Powered by KnowledgeTree" and retain the original
  34 + * copyright notice.
35 35 * Contributor( s): ______________________________________
36 36 *
37 37 */
38 38  
  39 + $webdav_pear_path = 'thirdparty/pear';
39 40 $kt_pear_path = '../thirdparty/pear';
40 41 $include_path = ini_get('include_path');
41   - ini_set('include_path', $kt_pear_path . PATH_SEPARATOR . $include_path);
  42 + ini_set('include_path', $webdav_pear_path . PATH_SEPARATOR . $kt_pear_path . PATH_SEPARATOR . $include_path);
42 43  
43 44 require_once "lib/KTWebDAVServer.inc.php";
44 45 $ktwebdav = new KTWebDAVServer();
... ...
lib/documentmanagement/documentutil.inc.php
... ... @@ -255,9 +255,6 @@ class KTDocumentUtil {
255 255 'creatorid' => $oUser->getID(),
256 256 'documenttypeid' => $iDocumentTypeId,
257 257 ));
258   - if (PEAR::isError($oDocument)) {
259   - return $oDocument;
260   - }
261 258  
262 259 if (is_null($oContents)) {
263 260 $res = KTDocumentUtil::setIncomplete($oDocument, 'contents');
... ... @@ -551,34 +548,15 @@ class KTDocumentUtil {
551 548 // {{{ _in_add
552 549 function &_in_add($oFolder, $sFilename, $oUser, $aOptions) {
553 550 $aOrigOptions = $aOptions;
554   - if (KTDocumentUtil::fileExists($oFolder, $sFilename)) {
555   - $oDoc = Document::getByFilenameAndFolder($sFilename, $oFolder->getId());
556   - if (PEAR::isError($oDoc)) {
557   - return PEAR::raiseError(_kt('Document with that filename already exists in this folder, and appears to be invalid. Please contact the system administrator.'));
558   - } else {
559   - if ($oDoc->getStatusID() != LIVE) {
560   - $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.');
561   - } else {
562   - $sError = _kt('Document with that filename already exists in this folder.');
563   - }
564   -
565   - $sError .= _kt(' Document') . ': ' . $oDoc->getName() . ' (ID:' . $oDoc->getId() . ')';
566   - return PEAR::raiseError($sError);
567   - }
  551 + while(KTDocumentUtil::fileExists($oFolder, $sFilename)) {
  552 + $oDoc = Document::getByFilenameAndFolder($sFilename, $oFolder->getId());
  553 + $sFilename = KTDocumentUtil::generateNewDocumentFilename($oDoc->getFileName());
568 554 }
569 555 $sName = KTUtil::arrayGet($aOptions, 'description', $sFilename);
570   - if (KTDocumentUtil::nameExists($oFolder, $sName)) {
571   - $oDoc = Document::getByNameAndFolder($sName, $oFolder->getId());
572   - if (PEAR::isError($oDoc)) {
573   - return PEAR::raiseError(_kt('Document with that title already exists in this folder, and appears to be invalid. Please contact the system administrator.'));
574   - } else {
575   - if ($oDoc->getStatusID != LIVE) {
576   - 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.'));
577   - } else {
578   - return PEAR::raiseError(_kt('Document with that title already exists in this folder.'));
579   - }
580   - }
581   -
  556 + while(KTDocumentUtil::nameExists($oFolder, $sName)) {
  557 + $oDoc = Document::getByNameAndFolder($sName, $oFolder->getId());
  558 + $aOptions['description'] = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
  559 + $sName = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
582 560 }
583 561  
584 562 $oUploadChannel =& KTUploadChannel::getSingleton();
... ... @@ -660,7 +638,46 @@ class KTDocumentUtil {
660 638 return $oDocument;
661 639 }
662 640 // }}}
663   -
  641 +
  642 + function generateNewDocumentFilename($sDocFilename){
  643 + if(preg_match("/\([0-9]+\)(\.[^\.]+){1,}$/", $sDocFilename)){
  644 + preg_match("/\([0-9]+\)\./", $sDocFilename, $matches);
  645 + $new_one = substr($matches[0], 1);
  646 + $new_two = explode(')', $new_one);
  647 + $new = $new_two[0]+1;
  648 +
  649 + $pattern[0] = '/\([0-9]+\)\./';
  650 + $replacement[0] = ' ('.$new.').';
  651 + $sFilename = preg_replace($pattern, $replacement, $sDocFilename);
  652 + }else{
  653 + $matches = explode('.', $sDocFilename);
  654 + $prefix = $matches[0].' (2)';
  655 + for($i = 1; $i < count($matches); $i++ ){
  656 + $suffix .= '.'.$matches[$i];
  657 + }
  658 + $sFilename = $prefix.$suffix;
  659 + }
  660 +
  661 + return $sFilename;
  662 + }
  663 +
  664 + function generateNewDocumentName($sDocName){
  665 + if(preg_match("/\([0-9]+\)$/", $sDocName)){
  666 + preg_match("/\([0-9]+\)$/", $sDocName, $matches);
  667 + $new_one = substr($matches[0], 1);
  668 + $new_two = explode(')', $new_one);
  669 + $new = $new_two[0]+1;
  670 +
  671 + $pattern[0] = '/\([0-9]+\)$/';
  672 + $replacement[0] = '('.$new.')';
  673 + $sName = preg_replace($pattern, $replacement, $sDocName);
  674 + }else{
  675 + $sName = $sDocName.' (2)';
  676 + }
  677 +
  678 + return $sName;
  679 + }
  680 +
664 681 // {{{ fileExists
665 682 function fileExists($oFolder, $sFilename) {
666 683 return Document::fileExists($sFilename, $oFolder->getID());
... ... @@ -1228,7 +1245,7 @@ class KTDocumentUtil {
1228 1245 class KTMetadataValidationError extends PEAR_Error {
1229 1246 function KTMetadataValidationError ($aFailed) {
1230 1247 $this->aFailed = $aFailed;
1231   - $message = _kt('Validation Failed');
  1248 + $message = _kt('Please be sure to enter information for all the Required fields below');
1232 1249 parent::PEAR_Error($message);
1233 1250 }
1234 1251 }
... ...
plugins/ktcore/KTBulkActions.php
... ... @@ -647,6 +647,9 @@ class KTBrowseBulkExportAction extends KTBulkAction {
647 647 $sFolderId = $oFolder->getId();
648 648 $sFolderDocs = $oFolder->getDocumentIDs($sFolderId);
649 649  
  650 + // Add folder to zip
  651 + $this->oZip->addFolderToZip($oFolder);
  652 +
650 653 if(!empty($sFolderDocs)){
651 654 $aDocuments = explode(',', $sFolderDocs);
652 655 }
... ... @@ -725,7 +728,13 @@ class KTBrowseBulkCheckoutAction extends KTBulkAction {
725 728 function check_entity($oEntity) {
726 729 if(is_a($oEntity, 'Document')) {
727 730 if ($oEntity->getIsCheckedOut()) {
728   - return PEAR::raiseError(_kt('Document is already checked out'));
  731 + $checkedOutUser = $oEntity->getCheckedOutUserID();
  732 + $sUserId = $_SESSION['userID'];
  733 +
  734 + if($checkedOutUser != $sUserId){
  735 + $oCheckedOutUser = User::get($checkedOutUser);
  736 + return PEAR::raiseError($oEntity->getName().': '._kt('Document has already been checked out by ').$oCheckedOutUser->getName());
  737 + }
729 738 }
730 739 }else if(!is_a($oEntity, 'Folder')) {
731 740 return PEAR::raiseError(_kt('Document cannot be checked out'));
... ... @@ -921,12 +930,29 @@ class KTBrowseBulkCheckoutAction extends KTBulkAction {
921 930 foreach($aDocuments as $sDocId){
922 931 $oDocument = Document::get($sDocId);
923 932 if(PEAR::isError($oDocument)) {
924   - return PEAR::raiseError(_kt('Folder documents cannot be checked out'));
  933 + // add message, skip document and continue
  934 + $this->addErrorMessage($oDocument->getName().': '.$oDocument->getMessage());
  935 + continue;
925 936 }
926 937  
  938 + // Checkout document - if it is already checked out, check the owner.
  939 + // If the current user is the owner, then include to the download, otherwise ignore.
927 940 $res = KTDocumentUtil::checkout($oDocument, $sReason, $this->oUser);
928 941 if(PEAR::isError($res)) {
929   - return PEAR::raiseError($oDocument->getName().': '.$res->getMessage());
  942 + if($oDocument->getIsCheckedOut()){
  943 + $checkedOutUser = $oDocument->getCheckedOutUserID();
  944 + $sUserId = $_SESSION['userID'];
  945 +
  946 + if($checkedOutUser != $sUserId){
  947 + $oCheckedOutUser = User::get($checkedOutUser);
  948 + $this->addErrorMessage($oDocument->getName().': '._kt('Document has already been checked out by ').$oCheckedOutUser->getName());
  949 + continue;
  950 + }
  951 + }
  952 + if($checkedOutUser != $sUserId){
  953 + $this->addErrorMessage($oDocument->getName().': '.$res->getMessage());
  954 + continue;
  955 + }
930 956 }
931 957  
932 958 // Add document to the zip file
... ...
plugins/ktcore/admin/groupManagement.php
... ... @@ -645,6 +645,7 @@ class KTGroupAdminDispatcher extends KTAdminDispatcher {
645 645 $data = $res['results'];
646 646 $errors = $res['errors'];
647 647 $extra_errors = array();
  648 +
648 649  
649 650 if (is_null($data['unit']) && $data['unitadmin']) {
650 651 $extra_errors['unitadmin'] = _kt("Groups without units cannot be Unit Administrators.");
... ... @@ -654,9 +655,21 @@ class KTGroupAdminDispatcher extends KTAdminDispatcher {
654 655 if (!PEAR::isError($oGroup)) {
655 656 $extra_errors['group_name'][] = _kt("There is already a group with that name.");
656 657 }
  658 +
  659 +
  660 + if(preg_match('/[\!\$\#\%\^\&\*]/', $data['group_name'])){
  661 + $extra_errors['group_name'][] = _kt("You have entered an invalid character.");
  662 + }
  663 +
  664 + if ($data['group_name'] == ''){
  665 + $extra_errors['group_name'][] = _kt("You have entered an invalid name.");
  666 + }
  667 +
657 668  
658 669 if (!empty($errors) || !empty($extra_errors)) {
  670 +
659 671 return $oForm->handleError(null, $extra_errors);
  672 +
660 673 }
661 674  
662 675 $this->startTransaction();
... ...
plugins/ktcore/admin/userManagement.php
... ... @@ -500,11 +500,21 @@ class KTUserAdminDispatcher extends KTAdminDispatcher {
500 500 $this->errorRedirectTo('addUser', _kt("The passwords you specified do not match."), sprintf("old_search=%s&do_search=1", $old_search));
501 501 }
502 502  
  503 + if(preg_match('/[\!\$\#\%\^\&\*]/', $username)){
  504 + $this->errorRedirectTo('addUser', _kt("You have entered an invalid character in your username."));
  505 + }
  506 +
  507 + if(preg_match('/[\!\$\#\%\^\&\*]/', $name)){
  508 + $this->errorRedirectTo('addUser', _kt("You have entered an invalid character in your name."));
  509 + }
  510 +
503 511 $dupUser =& User::getByUserName($username);
504 512 if(!PEAR::isError($dupUser)) {
505 513 $this->errorRedirectTo('addUser', _kt("A user with that username already exists"));
506 514 }
507 515  
  516 +
  517 +
508 518 $oUser =& User::createFromArray(array(
509 519 "sUsername" => $username,
510 520 "sName" => $name,
... ...
plugins/ktstandard/KTBulkExportPlugin.php
... ... @@ -65,7 +65,7 @@ class KTBulkExportAction extends KTFolderAction {
65 65 var $_sShowPermission = "ktcore.permissions.read";
66 66  
67 67 function getDisplayName() {
68   - return _kt('Bulk Export');
  68 + return _kt('Bulk Download');
69 69 }
70 70  
71 71 function do_main() {
... ...
plugins/rssplugin/RSSDedicatedDashlet.php 0 โ†’ 100644
  1 +<?php
  2 +/*
  3 + * $Id: RSSDashlet.php 7481 2007-10-23 09:47:11Z kevin_fourie $
  4 + *
  5 + * KnowledgeTree Open Source Edition
  6 + * Document Management Made Simple
  7 + * Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify it under
  10 + * the terms of the GNU General Public License version 3 as published by the
  11 + * Free Software Foundation.
  12 + *
  13 + * This program is distributed in the hope that it will be useful, but WITHOUT
  14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16 + * details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License
  19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 + *
  21 + * You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
  22 + * Blake Street, Observatory, 7925 South Africa. or email info@knowledgetree.com.
  23 + *
  24 + * The interactive user interfaces in modified source and object code versions
  25 + * of this program must display Appropriate Legal Notices, as required under
  26 + * Section 5 of the GNU General Public License version 3.
  27 + *
  28 + * In accordance with Section 7(b) of the GNU General Public License version 3,
  29 + * these Appropriate Legal Notices must retain the display of the "Powered by
  30 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  31 + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
  32 + * must display the words "Powered by KnowledgeTree" and retain the original
  33 + * copyright notice.
  34 + * Contributor( s): ______________________________________
  35 + *
  36 + */
  37 +
  38 +require_once(KT_LIB_DIR . '/browse/browseutil.inc.php');
  39 +require_once(KT_DIR. '/plugins/rssplugin/rss2array.inc.php');
  40 +require_once(KT_DIR. '/plugins/rssplugin/KTrss.inc.php');
  41 +
  42 +class RSSDedicatedDashlet extends KTBaseDashlet {
  43 + var $oUser;
  44 +
  45 + function RSSDedicatedDashlet(){
  46 + $oKTConfig =& KTConfig::getSingleton();
  47 +
  48 + $sTitle = $oKTConfig->get('urls/dedicatedrsstitle', 'RSS');
  49 + $this->sTitle = $sTitle;
  50 + }
  51 +
  52 + function is_active($oUser) {
  53 + $this->oUser = $oUser;
  54 + return true;
  55 + }
  56 +
  57 + function render() {
  58 + global $main;
  59 + $main->requireJSResource("plugins/rssplugin/js/update.js");
  60 +
  61 + $oTemplating =& KTTemplating::getSingleton();
  62 + $oTemplate = $oTemplating->loadTemplate('RSSPlugin/dedicated_dashlet');
  63 +
  64 + $iUId = $this->oUser->getId();
  65 + $oKTConfig =& KTConfig::getSingleton();
  66 +
  67 + $sUrl = $oKTConfig->get('urls/dedicatedrssurl', '');
  68 +
  69 + // Prepare template data
  70 + $aTemplateData = array(
  71 + 'context' => $this,
  72 + 'url' => $sUrl,
  73 + 'user' => $iUId,
  74 + );
  75 +
  76 + return $oTemplate->render($aTemplateData);
  77 + }
  78 +}
  79 +?>
... ...
plugins/rssplugin/RSSPlugin.php
1 1 <?php
2 2 /*
3   - * $Id:$
  3 + * $Id$
4 4 *
5 5 * KnowledgeTree Open Source Edition
6 6 * Document Management Made Simple
... ... @@ -59,6 +59,7 @@ require_once(&#39;RSSDocumentLinkAction.php&#39;);
59 59 $this->registerAction('folderaction', 'RSSFolderLinkAction', 'ktcore.rss.plugin.folder.link', $sFilename = null);
60 60 $this->registerAction('documentaction', 'RSSDocumentLinkAction', 'ktcore.rss.plugin.document.link', $sFilename = null);
61 61 $this->registerDashlet('RSSDashlet', 'ktcore.rss.feed.dashlet', 'RSSDashlet.php');
  62 + $this->registerDashlet('RSSDedicatedDashlet', 'ktcore.dedicated.rss.feed.dashlet', 'RSSDedicatedDashlet.php');
62 63 $this->registerPage('managerssfeeds', 'ManageRSSFeedsDispatcher');
63 64  
64 65 require_once(KT_LIB_DIR . "/templating/templating.inc.php");
... ...
plugins/rssplugin/js/update.js
... ... @@ -36,4 +36,43 @@ function GetXmlHttpObject(){
36 36 objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP");
37 37 }
38 38 return objXMLHttp;
  39 +}
  40 +
  41 +function loadDedicatedFeed(user){
  42 + xmlHttpD=GetXmlHttpObject();
  43 + if (xmlHttpD===null){
  44 + alert ("Browser does not support HTTP Request");
  45 + return;
  46 + }
  47 + var feed = FEED_URL;
  48 + // First check if there is a feed - in the event the 'Select feed' option was selected
  49 + if(feed !== 'null'){
  50 + var url=currloc+"plugins/rssplugin/loadDedicatedFeed.inc.php";
  51 + url=url+"?feed="+feed;
  52 + url=url+"&user="+user;
  53 + url=url+"&sid="+Math.random();
  54 + xmlHttpD.onreadystatechange=stateChangedD;
  55 + xmlHttpD.open("GET",url,true);
  56 + xmlHttpD.send(null);
  57 + }
  58 +}
  59 +
  60 +function stateChangedD(){
  61 + if (xmlHttpD.readyState==4 || xmlHttpD.readyState=="complete"){
  62 + document.getElementById("RSSDedicatedDashlet").style.display = "block";
  63 + document.getElementById("rssDedicatedBlock").innerHTML=xmlHttpD.responseText;
  64 + }else{
  65 + document.getElementById("RSSDedicatedDashlet").style.display = "block";
  66 + document.getElementById("rssDedicatedBlock").innerHTML="Loading feed...";
  67 + }
  68 +}
  69 +
  70 +function GetXmlHttpObjectD(){
  71 + var objXMLHttpD=null;
  72 + if (window.XMLHttpRequest){
  73 + objXMLHttpD=new XMLHttpRequest();
  74 + }else if (window.ActiveXObject){
  75 + objXMLHttpD=new ActiveXObject("Microsoft.XMLHTTP");
  76 + }
  77 + return objXMLHttpD;
39 78 }
40 79 \ No newline at end of file
... ...
plugins/rssplugin/loadDedicatedFeed.inc.php 0 โ†’ 100644
  1 +<?php
  2 +/*
  3 + * $Id: loadFeed.inc.php 7481 2007-10-23 09:47:11Z kevin_fourie $
  4 + *
  5 + * KnowledgeTree Open Source Edition
  6 + * Document Management Made Simple
  7 + * Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify it under
  10 + * the terms of the GNU General Public License version 3 as published by the
  11 + * Free Software Foundation.
  12 + *
  13 + * This program is distributed in the hope that it will be useful, but WITHOUT
  14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16 + * details.
  17 + *
  18 + * You should have received a copy of the GNU General Public License
  19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 + *
  21 + * You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
  22 + * Blake Street, Observatory, 7925 South Africa. or email info@knowledgetree.com.
  23 + *
  24 + * The interactive user interfaces in modified source and object code versions
  25 + * of this program must display Appropriate Legal Notices, as required under
  26 + * Section 5 of the GNU General Public License version 3.
  27 + *
  28 + * In accordance with Section 7(b) of the GNU General Public License version 3,
  29 + * these Appropriate Legal Notices must retain the display of the "Powered by
  30 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  31 + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
  32 + * must display the words "Powered by KnowledgeTree" and retain the original
  33 + * copyright notice.
  34 + * Contributor( s): ______________________________________
  35 + *
  36 + */
  37 + require_once('../../config/dmsDefaults.php');
  38 + require_once(KT_DIR. '/plugins/rssplugin/rss2array.inc.php');
  39 + require_once(KT_DIR. '/plugins/rssplugin/KTrss.inc.php');
  40 +
  41 + $feed = $_GET["feed"];
  42 + $user = $_GET["user"];
  43 + session_start();
  44 +
  45 + $sCache = _checkCache($feed);
  46 +
  47 + if(!is_null($sCache)) {
  48 + $aRSSArray = $sCache;
  49 + }else{
  50 + $aRSSArray = rss2array($feed);
  51 + $_SESSION['kt_dedicated_rss'][$feed]['lastcheck'] = time();
  52 + $_SESSION['kt_dedicated_rss'][$feed] = $aRSSArray;
  53 + }
  54 + // Prepare response data to be passed back to page
  55 + $reposonse = "<h3>".$aRSSArray[channel][title]."</h3>" .
  56 + "<div class='outerContainer' id='outerContainer'>" .
  57 + "<table width='90%'>";
  58 + for($i=0;$i<count($aRSSArray[items]);$i++){
  59 + $reposonse .= "<tr>
  60 + <td colspan='2'><strong><a href='".$aRSSArray[items][$i][link]."' target='_blank'>".$aRSSArray[items][$i][title]."</a><strong></td>
  61 + </tr>
  62 + <tr>
  63 + <td>".$aRSSArray[items][$i][description]."</td>
  64 + </tr>
  65 + <tr><td colspan='2'><br></td></tr>";
  66 + }
  67 + $reposonse .= "</table></div><br>";
  68 +
  69 + echo $reposonse;
  70 +
  71 + function _checkCache($feed) {
  72 + session_start();
  73 + $iLastCheck = $_SESSION['kt_dedicated_rss'][$feed]['lastcheck'];
  74 +
  75 + if (empty($iLastCheck)) {
  76 + return;
  77 + }
  78 + $sStoredFeed = $_SESSION['kt_dedicated_rss'][$feed];
  79 + if (empty($sStoredFeed)) {
  80 + $now = time();
  81 + $diff = $now - $iLastCheck;
  82 + if ($diff > (5*60)) {
  83 + return;
  84 + }
  85 + }
  86 + $now = time();
  87 + $diff = $now - $iLastCheck;
  88 + if ($diff > (5*60)) {
  89 + return;
  90 + }
  91 + return $sStoredFeed;
  92 +}
  93 +?>
... ...
plugins/rssplugin/templates/RSSPlugin/dedicated_dashlet.smarty 0 โ†’ 100644
  1 +{literal}
  2 +
  3 +<style type="text/css">
  4 +#RSSDedicatedDashlet {
  5 + display: none;
  6 +}
  7 +</style>
  8 +
  9 +{/literal}
  10 +<div id="rssDedicatedBlock">
  11 + <br>{i18n}RSS feed unavailable.{/i18n}.
  12 + <br>
  13 + <br>
  14 +</div>
  15 +{if $url != ''}
  16 +<script type="text/javascript">var FEED_URL = "{$url}";</script>
  17 +<script type="text/javascript">loadDedicatedFeed({$user});</script>
  18 +{/if}
0 19 \ No newline at end of file
... ...
sql/mysql/install/structure.sql
1 1 --
2 2 -- $Id$
3   ---
  3 +--
4 4 -- KnowledgeTree Open Source Edition
5 5 -- Document Management Made Simple
6 6 -- Copyright (C) 2004 - 2007 The Jam Warehouse Software (Pty) Limited
7   ---
  7 +--
8 8 -- This program is free software; you can redistribute it and/or modify it under
9 9 -- the terms of the GNU General Public License version 3 as published by the
10 10 -- Free Software Foundation.
11   ---
  11 +--
12 12 -- This program is distributed in the hope that it will be useful, but WITHOUT
13 13 -- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 14 -- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 15 -- details.
16   ---
  16 +--
17 17 -- You should have received a copy of the GNU General Public License
18 18 -- along with this program. If not, see <http://www.gnu.org/licenses/>.
19   ---
  19 +--
20 20 -- You can contact The Jam Warehouse Software (Pty) Limited, Unit 1, Tramber Place,
21 21 -- Blake Street, Observatory, 7925 South Africa. or email info@knowledgetree.com.
22   ---
  22 +--
23 23 -- The interactive user interfaces in modified source and object code versions
24 24 -- of this program must display Appropriate Legal Notices, as required under
25 25 -- Section 5 of the GNU General Public License version 3.
26   ---
  26 +--
27 27 -- In accordance with Section 7(b) of the GNU General Public License version 3,
28 28 -- these Appropriate Legal Notices must retain the display of the "Powered by
29   --- KnowledgeTree" logo and retain the original copyright notice. If the display of the
  29 +-- KnowledgeTree" logo and retain the original copyright notice. If the display of the
30 30 -- logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
31   --- must display the words "Powered by KnowledgeTree" and retain the original
  31 +-- must display the words "Powered by KnowledgeTree" and retain the original
32 32 -- copyright notice. -- Contributor( s): ______________________________________
33 33 --
34 34 -- MySQL dump 10.11
... ... @@ -204,13 +204,13 @@ CREATE TABLE `discussion_comments` (
204 204 --
205 205  
206 206 CREATE TABLE `discussion_threads` (
207   - `id` int(11) NOT NULL default '0',
208   - `document_id` int(11) NOT NULL default '0',
209   - `first_comment_id` int(11) NOT NULL default '0',
210   - `last_comment_id` int(11) NOT NULL default '0',
  207 + `id` int(11) NOT NULL,
  208 + `document_id` int(11) NOT NULL,
  209 + `first_comment_id` int(11) NULL,
  210 + `last_comment_id` int(11) NULL,
211 211 `views` int(11) NOT NULL default '0',
212 212 `replies` int(11) NOT NULL default '0',
213   - `creator_id` int(11) NOT NULL default '0',
  213 + `creator_id` int(11) NOT NULL,
214 214 `close_reason` mediumtext NOT NULL,
215 215 `close_metadata_version` int(11) NOT NULL default '0',
216 216 `state` int(1) NOT NULL default '0',
... ...
templates/ktcore/manage_help.smarty
1 1 <h2>{i18n}Current help assignments{/i18n}</h2>
2 2  
3   -<p class="descriptiveText">{i18n}To customise a help file, please visit that file
4   -via the help system and click on <strong>customise this help file</strong>.{/i18n}</p>
  3 +<p class="descriptiveText">{i18n}To customize a help file, please visit that file
  4 +via the help system and click on <strong>customize this help file</strong>.{/i18n}</p>
5 5  
6 6 { if $helpreplacements }
7   -<h2>{i18n}Existing customised help pages{/i18n}</h2>
  7 +<h2>{i18n}Existing customized help pages{/i18n}</h2>
8 8  
9 9 <table class="listing" cellspacing="0" cellpadding="0" border="0">
10 10 <thead>
... ... @@ -30,7 +30,7 @@ via the help system and click on &lt;strong&gt;customise this help file&lt;/strong&gt;.{/i18
30 30 </table>
31 31  
32 32 {else}
33   -<div class="ktInfoMessage"><span>{i18n}No help files have been customised.{/i18n}</span></div>
  33 +<div class="ktInfoMessage"><span>{i18n}No help files have been customized.{/i18n}</span></div>
34 34  
35 35 { /if }
36 36  
... ...
templates/ktstandard/disclaimers/manage_disclaimers.smarty
1 1 <h2>{i18n}Disclaimers{/i18n}</h2>
2 2  
3   -<p class="descriptiveText">{i18n}Please select a disclaimer to customise.{/i18n}</p>
  3 +<p class="descriptiveText">{i18n}Please select a disclaimer to customize.{/i18n}</p>
4 4  
5 5 <h2>{i18n}Existing disclaimers{/i18n}</h2>
6 6  
... ...
thirdparty/pear/HTTP/WebDAV/Server.php deleted
1   -<?php
2   -//
3   -// +----------------------------------------------------------------------+
4   -// | PHP Version 4 |
5   -// +----------------------------------------------------------------------+
6   -// | Copyright (c) 1997-2003 The PHP Group |
7   -// +----------------------------------------------------------------------+
8   -// | This source file is subject to version 2.02 of the PHP license, |
9   -// | that is bundled with this package in the file LICENSE, and is |
10   -// | available at through the world-wide-web at |
11   -// | http://www.php.net/license/2_02.txt. |
12   -// | If you did not receive a copy of the PHP license and are unable to |
13   -// | obtain it through the world-wide-web, please send a note to |
14   -// | license@php.net so we can mail you a copy immediately. |
15   -// +----------------------------------------------------------------------+
16   -// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
17   -// | Christian Stocker <chregu@bitflux.ch> |
18   -// +----------------------------------------------------------------------+
19   -//
20   -// $Id$
21   -//
22   -require_once "HTTP/WebDAV/Tools/_parse_propfind.php";
23   -require_once "HTTP/WebDAV/Tools/_parse_proppatch.php";
24   -require_once "HTTP/WebDAV/Tools/_parse_lockinfo.php";
25   -
26   -
27   -
28   -/**
29   - * Virtual base class for implementing WebDAV servers
30   - *
31   - * WebDAV server base class, needs to be extended to do useful work
32   - *
33   - * @package HTTP_WebDAV_Server
34   - * @author Hartmut Holzgraefe <hholzgra@php.net>
35   - * @version 0.99.1dev
36   - */
37   -class HTTP_WebDAV_Server
38   -{
39   - // {{{ Member Variables
40   -
41   - /**
42   - * complete URI for this request
43   - *
44   - * @var string
45   - */
46   - var $uri;
47   -
48   -
49   - /**
50   - * base URI for this request
51   - *
52   - * @var string
53   - */
54   - var $base_uri;
55   -
56   -
57   - /**
58   - * URI path for this request
59   - *
60   - * @var string
61   - */
62   - var $path;
63   -
64   - /**
65   - * Realm string to be used in authentification popups
66   - *
67   - * @var string
68   - */
69   - var $http_auth_realm = "PHP WebDAV";
70   -
71   - /**
72   - * String to be used in "X-Dav-Powered-By" header
73   - *
74   - * @var string
75   - */
76   - var $dav_powered_by = "";
77   -
78   - /**
79   - * Remember parsed If: (RFC2518/9.4) header conditions
80   - *
81   - * @var array
82   - */
83   - var $_if_header_uris = array();
84   -
85   - /**
86   - * HTTP response status/message
87   - *
88   - * @var string
89   - */
90   - var $_http_status = "200 OK";
91   -
92   - /**
93   - * encoding of property values passed in
94   - *
95   - * @var string
96   - */
97   - var $_prop_encoding = "utf-8";
98   -
99   - // }}}
100   -
101   - // {{{ Constructor
102   -
103   - /**
104   - * Constructor
105   - *
106   - * @param void
107   - */
108   - function HTTP_WebDAV_Server()
109   - {
110   - // PHP messages destroy XML output -> switch them off
111   - ini_set("display_errors", 0);
112   - }
113   -
114   - // }}}
115   -
116   - // {{{ ServeRequest()
117   - /**
118   - * Serve WebDAV HTTP request
119   - *
120   - * dispatch WebDAV HTTP request to the apropriate method handler
121   - *
122   - * @param void
123   - * @return void
124   - */
125   - function ServeRequest()
126   - {
127   - // default uri is the complete request uri
128   - $uri = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:");
129   - $uri.= "//$_SERVER[HTTP_HOST]$_SERVER[SCRIPT_NAME]";
130   -
131   - $this->base_uri = $uri;
132   - $this->uri = $uri . $_SERVER["PATH_INFO"];
133   -
134   - // identify ourselves
135   - if (empty($this->dav_powered_by)) {
136   - header("X-Dav-Powered-By: PHP class: ".get_class($this));
137   - } else {
138   - header("X-Dav-Powered-By: ".$this->dav_powered_by );
139   - }
140   -
141   - // check authentication
142   - if (!$this->_check_auth()) {
143   - // RFC2518 says we must use Digest instead of Basic
144   - // but Microsoft Clients do not support Digest
145   - // and we don't support NTLM and Kerberos
146   - // so we are stuck with Basic here
147   - header('WWW-Authenticate: Basic realm="'.($this->http_auth_realm).'"');
148   -
149   - // Windows seems to require this being the last header sent
150   - // (changed according to PECL bug #3138)
151   - $this->http_status('401 Unauthorized');
152   -
153   - return;
154   - }
155   -
156   - // check
157   - if(! $this->_check_if_header_conditions()) {
158   - $this->http_status("412 Precondition failed");
159   - return;
160   - }
161   -
162   - // set path
163   - $this->path = $this->_urldecode($_SERVER["PATH_INFO"]);
164   - if (!strlen($this->path)) {
165   - if ($_SERVER["REQUEST_METHOD"] == "GET") {
166   - // redirect clients that try to GET a collection
167   - // WebDAV clients should never try this while
168   - // regular HTTP clients might ...
169   - header("Location: ".$this->base_uri."/");
170   - exit;
171   - } else {
172   - // if a WebDAV client didn't give a path we just assume '/'
173   - $this->path = "/";
174   - }
175   - }
176   -
177   - if(ini_get("magic_quotes_gpc")) {
178   - $this->path = stripslashes($this->path);
179   - }
180   -
181   -
182   - // detect requested method names
183   - $method = strtolower($_SERVER["REQUEST_METHOD"]);
184   - $wrapper = "http_".$method;
185   -
186   - // activate HEAD emulation by GET if no HEAD method found
187   - if ($method == "head" && !method_exists($this, "head")) {
188   - $method = "get";
189   - }
190   -
191   - if (method_exists($this, $wrapper) && ($method == "options" || method_exists($this, $method))) {
192   - $this->$wrapper(); // call method by name
193   - } else { // method not found/implemented
194   - if ($_SERVER["REQUEST_METHOD"] == "LOCK") {
195   - $this->http_status("412 Precondition failed");
196   - } else {
197   - $this->http_status("405 Method not allowed");
198   - header("Allow: ".join(", ", $this->_allow())); // tell client what's allowed
199   - }
200   - }
201   - }
202   -
203   - // }}}
204   -
205   - // {{{ abstract WebDAV methods
206   -
207   - // {{{ GET()
208   - /**
209   - * GET implementation
210   - *
211   - * overload this method to retrieve resources from your server
212   - * <br>
213   - *
214   - *
215   - * @abstract
216   - * @param array &$params Array of input and output parameters
217   - * <br><b>input</b><ul>
218   - * <li> path -
219   - * </ul>
220   - * <br><b>output</b><ul>
221   - * <li> size -
222   - * </ul>
223   - * @returns int HTTP-Statuscode
224   - */
225   -
226   - /* abstract
227   - function GET(&$params)
228   - {
229   - // dummy entry for PHPDoc
230   - }
231   - */
232   -
233   - // }}}
234   -
235   - // {{{ PUT()
236   - /**
237   - * PUT implementation
238   - *
239   - * PUT implementation
240   - *
241   - * @abstract
242   - * @param array &$params
243   - * @returns int HTTP-Statuscode
244   - */
245   -
246   - /* abstract
247   - function PUT()
248   - {
249   - // dummy entry for PHPDoc
250   - }
251   - */
252   -
253   - // }}}
254   -
255   - // {{{ COPY()
256   -
257   - /**
258   - * COPY implementation
259   - *
260   - * COPY implementation
261   - *
262   - * @abstract
263   - * @param array &$params
264   - * @returns int HTTP-Statuscode
265   - */
266   -
267   - /* abstract
268   - function COPY()
269   - {
270   - // dummy entry for PHPDoc
271   - }
272   - */
273   -
274   - // }}}
275   -
276   - // {{{ MOVE()
277   -
278   - /**
279   - * MOVE implementation
280   - *
281   - * MOVE implementation
282   - *
283   - * @abstract
284   - * @param array &$params
285   - * @returns int HTTP-Statuscode
286   - */
287   -
288   - /* abstract
289   - function MOVE()
290   - {
291   - // dummy entry for PHPDoc
292   - }
293   - */
294   -
295   - // }}}
296   -
297   - // {{{ DELETE()
298   -
299   - /**
300   - * DELETE implementation
301   - *
302   - * DELETE implementation
303   - *
304   - * @abstract
305   - * @param array &$params
306   - * @returns int HTTP-Statuscode
307   - */
308   -
309   - /* abstract
310   - function DELETE()
311   - {
312   - // dummy entry for PHPDoc
313   - }
314   - */
315   - // }}}
316   -
317   - // {{{ PROPFIND()
318   -
319   - /**
320   - * PROPFIND implementation
321   - *
322   - * PROPFIND implementation
323   - *
324   - * @abstract
325   - * @param array &$params
326   - * @returns int HTTP-Statuscode
327   - */
328   -
329   - /* abstract
330   - function PROPFIND()
331   - {
332   - // dummy entry for PHPDoc
333   - }
334   - */
335   -
336   - // }}}
337   -
338   - // {{{ PROPPATCH()
339   -
340   - /**
341   - * PROPPATCH implementation
342   - *
343   - * PROPPATCH implementation
344   - *
345   - * @abstract
346   - * @param array &$params
347   - * @returns int HTTP-Statuscode
348   - */
349   -
350   - /* abstract
351   - function PROPPATCH()
352   - {
353   - // dummy entry for PHPDoc
354   - }
355   - */
356   - // }}}
357   -
358   - // {{{ LOCK()
359   -
360   - /**
361   - * LOCK implementation
362   - *
363   - * LOCK implementation
364   - *
365   - * @abstract
366   - * @param array &$params
367   - * @returns int HTTP-Statuscode
368   - */
369   -
370   - /* abstract
371   - function LOCK()
372   - {
373   - // dummy entry for PHPDoc
374   - }
375   - */
376   - // }}}
377   -
378   - // {{{ UNLOCK()
379   -
380   - /**
381   - * UNLOCK implementation
382   - *
383   - * UNLOCK implementation
384   - *
385   - * @abstract
386   - * @param array &$params
387   - * @returns int HTTP-Statuscode
388   - */
389   -
390   - /* abstract
391   - function UNLOCK()
392   - {
393   - // dummy entry for PHPDoc
394   - }
395   - */
396   - // }}}
397   -
398   - // }}}
399   -
400   - // {{{ other abstract methods
401   -
402   - // {{{ check_auth()
403   -
404   - /**
405   - * check authentication
406   - *
407   - * overload this method to retrieve and confirm authentication information
408   - *
409   - * @abstract
410   - * @param string type Authentication type, e.g. "basic" or "digest"
411   - * @param string username Transmitted username
412   - * @param string passwort Transmitted password
413   - * @returns bool Authentication status
414   - */
415   -
416   - /* abstract
417   - function checkAuth($type, $username, $password)
418   - {
419   - // dummy entry for PHPDoc
420   - }
421   - */
422   -
423   - // }}}
424   -
425   - // {{{ checklock()
426   -
427   - /**
428   - * check lock status for a resource
429   - *
430   - * overload this method to return shared and exclusive locks
431   - * active for this resource
432   - *
433   - * @abstract
434   - * @param string resource Resource path to check
435   - * @returns array An array of lock entries each consisting
436   - * of 'type' ('shared'/'exclusive'), 'token' and 'timeout'
437   - */
438   -
439   - /* abstract
440   - function checklock($resource)
441   - {
442   - // dummy entry for PHPDoc
443   - }
444   - */
445   -
446   - // }}}
447   -
448   - // }}}
449   -
450   - // {{{ WebDAV HTTP method wrappers
451   -
452   - // {{{ http_OPTIONS()
453   -
454   - /**
455   - * OPTIONS method handler
456   - *
457   - * The OPTIONS method handler creates a valid OPTIONS reply
458   - * including Dav: and Allowed: heaers
459   - * based on the implemented methods found in the actual instance
460   - *
461   - * @param void
462   - * @return void
463   - */
464   - function http_OPTIONS()
465   - {
466   - // Microsoft clients default to the Frontpage protocol
467   - // unless we tell them to use WebDAV
468   - header("MS-Author-Via: DAV");
469   -
470   - // get allowed methods
471   - $allow = $this->_allow();
472   -
473   - // dav header
474   - $dav = array(1); // assume we are always dav class 1 compliant
475   - if (isset($allow['LOCK'])) {
476   - $dav[] = 2; // dav class 2 requires that locking is supported
477   - }
478   -
479   - // tell clients what we found
480   - $this->http_status("200 OK");
481   - header("DAV: " .join("," , $dav));
482   - header("Allow: ".join(", ", $allow));
483   -
484   - header("Content-length: 0");
485   - }
486   -
487   - // }}}
488   -
489   -
490   - // {{{ http_PROPFIND()
491   -
492   - /**
493   - * PROPFIND method handler
494   - *
495   - * @param void
496   - * @return void
497   - */
498   - function http_PROPFIND()
499   - {
500   - $options = Array();
501   - $options["path"] = $this->path;
502   -
503   - // search depth from header (default is "infinity)
504   - if (isset($_SERVER['HTTP_DEPTH'])) {
505   - $options["depth"] = $_SERVER["HTTP_DEPTH"];
506   - } else {
507   - $options["depth"] = "infinity";
508   - }
509   -
510   - // analyze request payload
511   - $propinfo = new _parse_propfind("php://input");
512   - if (!$propinfo->success) {
513   - $this->http_status("400 Error");
514   - return;
515   - }
516   - $options['props'] = $propinfo->props;
517   -
518   - // call user handler
519   - if (!$this->PROPFIND($options, $files)) {
520   - $this->http_status("404 Not Found");
521   - return;
522   - }
523   -
524   - // collect namespaces here
525   - $ns_hash = array();
526   -
527   - // Microsoft Clients need this special namespace for date and time values
528   - $ns_defs = "xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\"";
529   -
530   - // now we loop over all returned file entries
531   - foreach($files["files"] as $filekey => $file) {
532   -
533   - // nothing to do if no properties were returend for a file
534   - if (!isset($file["props"]) || !is_array($file["props"])) {
535   - continue;
536   - }
537   -
538   - // now loop over all returned properties
539   - foreach($file["props"] as $key => $prop) {
540   - // as a convenience feature we do not require that user handlers
541   - // restrict returned properties to the requested ones
542   - // here we strip all unrequested entries out of the response
543   -
544   - switch($options['props']) {
545   - case "all":
546   - // nothing to remove
547   - break;
548   -
549   - case "names":
550   - // only the names of all existing properties were requested
551   - // so we remove all values
552   - unset($files["files"][$filekey]["props"][$key]["val"]);
553   - break;
554   -
555   - default:
556   - $found = false;
557   -
558   - // search property name in requested properties
559   - foreach((array)$options["props"] as $reqprop) {
560   - if ( $reqprop["name"] == $prop["name"]
561   - && $reqprop["xmlns"] == $prop["ns"]) {
562   - $found = true;
563   - break;
564   - }
565   - }
566   -
567   - // unset property and continue with next one if not found/requested
568   - if (!$found) {
569   - $files["files"][$filekey]["props"][$key]="";
570   - continue(2);
571   - }
572   - break;
573   - }
574   -
575   - // namespace handling
576   - if (empty($prop["ns"])) continue; // no namespace
577   - $ns = $prop["ns"];
578   - if ($ns == "DAV:") continue; // default namespace
579   - if (isset($ns_hash[$ns])) continue; // already known
580   -
581   - // register namespace
582   - $ns_name = "ns".(count($ns_hash) + 1);
583   - $ns_hash[$ns] = $ns_name;
584   - $ns_defs .= " xmlns:$ns_name=\"$ns\"";
585   - }
586   -
587   - // we also need to add empty entries for properties that were requested
588   - // but for which no values where returned by the user handler
589   - if (is_array($options['props'])) {
590   - foreach($options["props"] as $reqprop) {
591   - if($reqprop['name']=="") continue; // skip empty entries
592   -
593   - $found = false;
594   -
595   - // check if property exists in result
596   - foreach($file["props"] as $prop) {
597   - if ( $reqprop["name"] == $prop["name"]
598   - && $reqprop["xmlns"] == $prop["ns"]) {
599   - $found = true;
600   - break;
601   - }
602   - }
603   -
604   - if (!$found) {
605   - if($reqprop["xmlns"]==="DAV:" && $reqprop["name"]==="lockdiscovery") {
606   - // lockdiscovery is handled by the base class
607   - $files["files"][$filekey]["props"][]
608   - = $this->mkprop("DAV:",
609   - "lockdiscovery" ,
610   - $this->lockdiscovery($files["files"][$filekey]['path']));
611   - } else {
612   - // add empty value for this property
613   - $files["files"][$filekey]["noprops"][] =
614   - $this->mkprop($reqprop["xmlns"], $reqprop["name"], "");
615   -
616   - // register property namespace if not known yet
617   - if ($reqprop["xmlns"] != "DAV:" && !isset($ns_hash[$reqprop["xmlns"]])) {
618   - $ns_name = "ns".(count($ns_hash) + 1);
619   - $ns_hash[$reqprop["xmlns"]] = $ns_name;
620   - $ns_defs .= " xmlns:$ns_name=\"$reqprop[xmlns]\"";
621   - }
622   - }
623   - }
624   - }
625   - }
626   - }
627   -
628   - // now we generate the reply header ...
629   - $this->http_status("207 Multi-Status");
630   - header('Content-Type: text/xml; charset="utf-8"');
631   -
632   - // ... and payload
633   - echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
634   - echo "<D:multistatus xmlns:D=\"DAV:\">\n";
635   -
636   - foreach($files["files"] as $file) {
637   - // ignore empty or incomplete entries
638   - if(!is_array($file) || empty($file) || !isset($file["path"])) continue;
639   - $path = $file['path'];
640   - if(!is_string($path) || $path==="") continue;
641   -
642   - echo " <D:response $ns_defs>\n";
643   -
644   - $href = $this->_slashify($this->_mergePathes($_SERVER['SCRIPT_NAME'], $path));
645   -
646   - echo " <D:href>$href</D:href>\n";
647   -
648   - // report all found properties and their values (if any)
649   - if (isset($file["props"]) && is_array($file["props"])) {
650   - echo " <D:propstat>\n";
651   - echo " <D:prop>\n";
652   -
653   - foreach($file["props"] as $key => $prop) {
654   -
655   - if (!is_array($prop)) continue;
656   - if (!isset($prop["name"])) continue;
657   -
658   - if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) {
659   - // empty properties (cannot use empty() for check as "0" is a legal value here)
660   - if($prop["ns"]=="DAV:") {
661   - echo " <D:$prop[name]/>\n";
662   - } else if(!empty($prop["ns"])) {
663   - echo " <".$ns_hash[$prop["ns"]].":$prop[name]/>\n";
664   - } else {
665   - echo " <$prop[name] xmlns=\"\"/>";
666   - }
667   - } else if ($prop["ns"] == "DAV:") {
668   - // some WebDAV properties need special treatment
669   - switch ($prop["name"]) {
670   - case "creationdate":
671   - echo " <D:creationdate ns0:dt=\"dateTime.tz\">"
672   - . gmdate("Y-m-d\\TH:i:s\\Z",$prop['val'])
673   - . "</D:creationdate>\n";
674   - break;
675   - case "getlastmodified":
676   - echo " <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"
677   - . gmdate("D, d M Y H:i:s ", $prop['val'])
678   - . "GMT</D:getlastmodified>\n";
679   - break;
680   - case "resourcetype":
681   - echo " <D:resourcetype><D:$prop[val]/></D:resourcetype>\n";
682   - break;
683   - case "supportedlock":
684   - echo " <D:supportedlock>$prop[val]</D:supportedlock>\n";
685   - break;
686   - case "lockdiscovery":
687   - echo " <D:lockdiscovery>\n";
688   - echo $prop["val"];
689   - echo " </D:lockdiscovery>\n";
690   - break;
691   - default:
692   - echo " <D:$prop[name]>"
693   - . $this->_prop_encode(htmlspecialchars($prop['val']))
694   - . "</D:$prop[name]>\n";
695   - break;
696   - }
697   - } else {
698   - // properties from namespaces != "DAV:" or without any namespace
699   - if ($prop["ns"]) {
700   - echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>"
701   - . $this->_prop_encode(htmlspecialchars($prop['val']))
702   - . "</" . $ns_hash[$prop["ns"]] . ":$prop[name]>\n";
703   - } else {
704   - echo " <$prop[name] xmlns=\"\">"
705   - . $this->_prop_encode(htmlspecialchars($prop['val']))
706   - . "</$prop[name]>\n";
707   - }
708   - }
709   - }
710   -
711   - echo " </D:prop>\n";
712   - echo " <D:status>HTTP/1.1 200 OK</D:status>\n";
713   - echo " </D:propstat>\n";
714   - }
715   -
716   - // now report all properties requested but not found
717   - if (isset($file["noprops"])) {
718   - echo " <D:propstat>\n";
719   - echo " <D:prop>\n";
720   -
721   - foreach($file["noprops"] as $key => $prop) {
722   - if ($prop["ns"] == "DAV:") {
723   - echo " <D:$prop[name]/>\n";
724   - } else if ($prop["ns"] == "") {
725   - echo " <$prop[name] xmlns=\"\"/>\n";
726   - } else {
727   - echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]/>\n";
728   - }
729   - }
730   -
731   - echo " </D:prop>\n";
732   - echo " <D:status>HTTP/1.1 404 Not Found</D:status>\n";
733   - echo " </D:propstat>\n";
734   - }
735   -
736   - echo " </D:response>\n";
737   - }
738   -
739   - echo "</D:multistatus>\n";
740   - }
741   -
742   -
743   - // }}}
744   -
745   - // {{{ http_PROPPATCH()
746   -
747   - /**
748   - * PROPPATCH method handler
749   - *
750   - * @param void
751   - * @return void
752   - */
753   - function http_PROPPATCH()
754   - {
755   - if($this->_check_lock_status($this->path)) {
756   - $options = Array();
757   - $options["path"] = $this->path;
758   -
759   - $propinfo = new _parse_proppatch("php://input");
760   -
761   - if (!$propinfo->success) {
762   - $this->http_status("400 Error");
763   - return;
764   - }
765   -
766   - $options['props'] = $propinfo->props;
767   -
768   - $responsedescr = $this->PROPPATCH($options);
769   -
770   - $this->http_status("207 Multi-Status");
771   - header('Content-Type: text/xml; charset="utf-8"');
772   -
773   - echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
774   -
775   - echo "<D:multistatus xmlns:D=\"DAV:\">\n";
776   - echo " <D:response>\n";
777   - echo " <D:href>".$this->_urlencode($this->_mergePathes($_SERVER["SCRIPT_NAME"], $this->path))."</D:href>\n";
778   -
779   - foreach($options["props"] as $prop) {
780   - echo " <D:propstat>\n";
781   - echo " <D:prop><$prop[name] xmlns=\"$prop[ns]\"/></D:prop>\n";
782   - echo " <D:status>HTTP/1.1 $prop[status]</D:status>\n";
783   - echo " </D:propstat>\n";
784   - }
785   -
786   - if ($responsedescr) {
787   - echo " <D:responsedescription>".
788   - $this->_prop_encode(htmlspecialchars($responsedescr)).
789   - "</D:responsedescription>\n";
790   - }
791   -
792   - echo " </D:response>\n";
793   - echo "</D:multistatus>\n";
794   - } else {
795   - $this->http_status("423 Locked");
796   - }
797   - }
798   -
799   - // }}}
800   -
801   -
802   - // {{{ http_MKCOL()
803   -
804   - /**
805   - * MKCOL method handler
806   - *
807   - * @param void
808   - * @return void
809   - */
810   - function http_MKCOL()
811   - {
812   - $options = Array();
813   - $options["path"] = $this->path;
814   -
815   - $stat = $this->MKCOL($options);
816   -
817   - $this->http_status($stat);
818   - }
819   -
820   - // }}}
821   -
822   -
823   - // {{{ http_GET()
824   -
825   - /**
826   - * GET method handler
827   - *
828   - * @param void
829   - * @returns void
830   - */
831   - function http_GET()
832   - {
833   - // TODO check for invalid stream
834   - $options = Array();
835   - $options["path"] = $this->path;
836   -
837   - $this->_get_ranges($options);
838   -
839   - if (true === ($status = $this->GET($options))) {
840   - if (!headers_sent()) {
841   - $status = "200 OK";
842   -
843   - if (!isset($options['mimetype'])) {
844   - $options['mimetype'] = "application/octet-stream";
845   - }
846   - header("Content-type: $options[mimetype]");
847   -
848   - if (isset($options['mtime'])) {
849   - header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT");
850   - }
851   -
852   - if (isset($options['stream'])) {
853   - // GET handler returned a stream
854   - if (!empty($options['ranges']) && (0===fseek($options['stream'], 0, SEEK_SET))) {
855   - // partial request and stream is seekable
856   -
857   - if (count($options['ranges']) === 1) {
858   - $range = $options['ranges'][0];
859   -
860   - if (isset($range['start'])) {
861   - fseek($options['stream'], $range['start'], SEEK_SET);
862   - if (feof($options['stream'])) {
863   - $this->http_status("416 Requested range not satisfiable");
864   - exit;
865   - }
866   -
867   - if (isset($range['end'])) {
868   - $size = $range['end']-$range['start']+1;
869   - $this->http_status("206 partial");
870   - header("Content-length: $size");
871   - header("Content-range: $range[start]-$range[end]/"
872   - . (isset($options['size']) ? $options['size'] : "*"));
873   - while ($size && !feof($options['stream'])) {
874   - $buffer = fread($options['stream'], 4096);
875   - $size -= strlen($buffer);
876   - echo $buffer;
877   - }
878   - } else {
879   - $this->http_status("206 partial");
880   - if (isset($options['size'])) {
881   - header("Content-length: ".($options['size'] - $range['start']));
882   - header("Content-range: $start-$end/"
883   - . (isset($options['size']) ? $options['size'] : "*"));
884   - }
885   - fpassthru($options['stream']);
886   - }
887   - } else {
888   - header("Content-length: ".$range['last']);
889   - fseek($options['stream'], -$range['last'], SEEK_END);
890   - fpassthru($options['stream']);
891   - }
892   - } else {
893   - $this->_multipart_byterange_header(); // init multipart
894   - foreach ($options['ranges'] as $range) {
895   - // TODO what if size unknown? 500?
896   - if (isset($range['start'])) {
897   - $from = $range['start'];
898   - $to = !empty($range['end']) ? $range['end'] : $options['size']-1;
899   - } else {
900   - $from = $options['size'] - $range['last']-1;
901   - $to = $options['size'] -1;
902   - }
903   - $total = isset($options['size']) ? $options['size'] : "*";
904   - $size = $to - $from + 1;
905   - $this->_multipart_byterange_header($options['mimetype'], $from, $to, $total);
906   -
907   -
908   - fseek($options['stream'], $start, SEEK_SET);
909   - while ($size && !feof($options['stream'])) {
910   - $buffer = fread($options['stream'], 4096);
911   - $size -= strlen($buffer);
912   - echo $buffer;
913   - }
914   - }
915   - $this->_multipart_byterange_header(); // end multipart
916   - }
917   - } else {
918   - // normal request or stream isn't seekable, return full content
919   - if (isset($options['size'])) {
920   - header("Content-length: ".$options['size']);
921   - }
922   - fpassthru($options['stream']);
923   - return; // no more headers
924   - }
925   - } elseif (isset($options['data'])) {
926   - if (is_array($options['data'])) {
927   - // reply to partial request
928   - } else {
929   - header("Content-length: ".strlen($options['data']));
930   - echo $options['data'];
931   - }
932   - }
933   - }
934   - }
935   -
936   - if (!headers_sent()) {
937   - if (false === $status) {
938   - $this->http_status("404 not found");
939   - }
940   -
941   - // TODO: check setting of headers in various code pathes above
942   - $this->http_status("$status");
943   - }
944   - }
945   -
946   -
947   - /**
948   - * parse HTTP Range: header
949   - *
950   - * @param array options array to store result in
951   - * @return void
952   - */
953   - function _get_ranges(&$options)
954   - {
955   - // process Range: header if present
956   - if (isset($_SERVER['HTTP_RANGE'])) {
957   -
958   - // we only support standard "bytes" range specifications for now
959   - if (ereg("bytes[[:space:]]*=[[:space:]]*(.+)", $_SERVER['HTTP_RANGE'], $matches)) {
960   - $options["ranges"] = array();
961   -
962   - // ranges are comma separated
963   - foreach (explode(",", $matches[1]) as $range) {
964   - // ranges are either from-to pairs or just end positions
965   - list($start, $end) = explode("-", $range);
966   - $options["ranges"][] = ($start==="")
967   - ? array("last"=>$end)
968   - : array("start"=>$start, "end"=>$end);
969   - }
970   - }
971   - }
972   - }
973   -
974   - /**
975   - * generate separator headers for multipart response
976   - *
977   - * first and last call happen without parameters to generate
978   - * the initial header and closing sequence, all calls inbetween
979   - * require content mimetype, start and end byte position and
980   - * optionaly the total byte length of the requested resource
981   - *
982   - * @param string mimetype
983   - * @param int start byte position
984   - * @param int end byte position
985   - * @param int total resource byte size
986   - */
987   - function _multipart_byterange_header($mimetype = false, $from = false, $to=false, $total=false)
988   - {
989   - if ($mimetype === false) {
990   - if (!isset($this->multipart_separator)) {
991   - // initial
992   -
993   - // a little naive, this sequence *might* be part of the content
994   - // but it's really not likely and rather expensive to check
995   - $this->multipart_separator = "SEPARATOR_".md5(microtime());
996   -
997   - // generate HTTP header
998   - header("Content-type: multipart/byteranges; boundary=".$this->multipart_separator);
999   - } else {
1000   - // final
1001   -
1002   - // generate closing multipart sequence
1003   - echo "\n--{$this->multipart_separator}--";
1004   - }
1005   - } else {
1006   - // generate separator and header for next part
1007   - echo "\n--{$this->multipart_separator}\n";
1008   - echo "Content-type: $mimetype\n";
1009   - echo "Content-range: $from-$to/". ($total === false ? "*" : $total);
1010   - echo "\n\n";
1011   - }
1012   - }
1013   -
1014   -
1015   -
1016   - // }}}
1017   -
1018   - // {{{ http_HEAD()
1019   -
1020   - /**
1021   - * HEAD method handler
1022   - *
1023   - * @param void
1024   - * @return void
1025   - */
1026   - function http_HEAD()
1027   - {
1028   - $status = false;
1029   - $options = Array();
1030   - $options["path"] = $this->path;
1031   -
1032   - if (method_exists($this, "HEAD")) {
1033   - $status = $this->head($options);
1034   - } else if (method_exists($this, "GET")) {
1035   - ob_start();
1036   - $status = $this->GET($options);
1037   - ob_end_clean();
1038   - }
1039   -
1040   - if($status===true) $status = "200 OK";
1041   - if($status===false) $status = "404 Not found";
1042   -
1043   - $this->http_status($status);
1044   - }
1045   -
1046   - // }}}
1047   -
1048   - // {{{ http_PUT()
1049   -
1050   - /**
1051   - * PUT method handler
1052   - *
1053   - * @param void
1054   - * @return void
1055   - */
1056   - function http_PUT()
1057   - {
1058   - if ($this->_check_lock_status($this->path)) {
1059   - $options = Array();
1060   - $options["path"] = $this->path;
1061   - $options["content_length"] = $_SERVER["CONTENT_LENGTH"];
1062   -
1063   - // get the Content-type
1064   - if (isset($_SERVER["CONTENT_TYPE"])) {
1065   - // for now we do not support any sort of multipart requests
1066   - if (!strncmp($_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
1067   - $this->http_status("501 not implemented");
1068   - echo "The service does not support mulipart PUT requests";
1069   - return;
1070   - }
1071   - $options["content_type"] = $_SERVER["CONTENT_TYPE"];
1072   - } else {
1073   - // default content type if none given
1074   - $options["content_type"] = "application/octet-stream";
1075   - }
1076   -
1077   - /* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
1078   - ignore any Content-* (e.g. Content-Range) headers that it
1079   - does not understand or implement and MUST return a 501
1080   - (Not Implemented) response in such cases."
1081   - */
1082   - foreach ($_SERVER as $key => $val) {
1083   - if (strncmp($key, "HTTP_CONTENT", 11)) continue;
1084   - switch ($key) {
1085   - case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
1086   - // TODO support this if ext/zlib filters are available
1087   - $this->http_status("501 not implemented");
1088   - echo "The service does not support '$val' content encoding";
1089   - return;
1090   -
1091   - case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
1092   - // we assume it is not critical if this one is ignored
1093   - // in the actual PUT implementation ...
1094   - $options["content_language"] = $value;
1095   - break;
1096   -
1097   - case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
1098   - /* The meaning of the Content-Location header in PUT
1099   - or POST requests is undefined; servers are free
1100   - to ignore it in those cases. */
1101   - break;
1102   -
1103   - case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
1104   - // single byte range requests are supported
1105   - // the header format is also specified in RFC 2616 14.16
1106   - // TODO we have to ensure that implementations support this or send 501 instead
1107   - if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1108   - $this->http_status("400 bad request");
1109   - echo "The service does only support single byte ranges";
1110   - return;
1111   - }
1112   -
1113   - $range = array("start"=>$matches[1], "end"=>$matches[2]);
1114   - if (is_numeric($matches[3])) {
1115   - $range["total_length"] = $matches[3];
1116   - }
1117   - $option["ranges"][] = $range;
1118   -
1119   - // TODO make sure the implementation supports partial PUT
1120   - // this has to be done in advance to avoid data being overwritten
1121   - // on implementations that do not support this ...
1122   - break;
1123   -
1124   - case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
1125   - // TODO: maybe we can just pretend here?
1126   - $this->http_status("501 not implemented");
1127   - echo "The service does not support content MD5 checksum verification";
1128   - return;
1129   -
1130   - default:
1131   - // any other unknown Content-* headers
1132   - $this->http_status("501 not implemented");
1133   - echo "The service does not support '$key'";
1134   - return;
1135   - }
1136   - }
1137   -
1138   - $options["stream"] = fopen("php://input", "r");
1139   -
1140   - $stat = $this->PUT($options);
1141   -
1142   - if ($stat === false) {
1143   - $stat = "403 Forbidden";
1144   - } else if (is_resource($stat) && get_resource_type($stat) == "stream") {
1145   - $stream = $stat;
1146   -
1147   - $stat = $options["new"] ? "201 Created" : "204 No Content";
1148   -
1149   - if (!empty($options["ranges"])) {
1150   - // TODO multipart support is missing (see also above)
1151   - if (0 == fseek($stream, $range[0]["start"], SEEK_SET)) {
1152   - $length = $range[0]["end"]-$range[0]["start"]+1;
1153   - if (!fwrite($stream, fread($options["stream"], $length))) {
1154   - $stat = "403 Forbidden";
1155   - }
1156   - } else {
1157   - $stat = "403 Forbidden";
1158   - }
1159   - } else {
1160   - while (!feof($options["stream"])) {
1161   - if (false === fwrite($stream, fread($options["stream"], 4096))) {
1162   - $stat = "403 Forbidden";
1163   - break;
1164   - }
1165   - }
1166   - }
1167   -
1168   - fclose($stream);
1169   - }
1170   -
1171   - $this->http_status($stat);
1172   - } else {
1173   - $this->http_status("423 Locked");
1174   - }
1175   - }
1176   -
1177   - // }}}
1178   -
1179   -
1180   - // {{{ http_DELETE()
1181   -
1182   - /**
1183   - * DELETE method handler
1184   - *
1185   - * @param void
1186   - * @return void
1187   - */
1188   - function http_DELETE()
1189   - {
1190   - // check RFC 2518 Section 9.2, last paragraph
1191   - if (isset($_SERVER["HTTP_DEPTH"])) {
1192   - if ($_SERVER["HTTP_DEPTH"] != "infinity") {
1193   - $this->http_status("400 Bad Request");
1194   - return;
1195   - }
1196   - }
1197   -
1198   - // check lock status
1199   - if ($this->_check_lock_status($this->path)) {
1200   - // ok, proceed
1201   - $options = Array();
1202   - $options["path"] = $this->path;
1203   -
1204   - $stat = $this->DELETE($options);
1205   -
1206   - $this->http_status($stat);
1207   - } else {
1208   - // sorry, its locked
1209   - $this->http_status("423 Locked");
1210   - }
1211   - }
1212   -
1213   - // }}}
1214   -
1215   - // {{{ http_COPY()
1216   -
1217   - /**
1218   - * COPY method handler
1219   - *
1220   - * @param void
1221   - * @return void
1222   - */
1223   - function http_COPY()
1224   - {
1225   - // no need to check source lock status here
1226   - // destination lock status is always checked by the helper method
1227   - $this->_copymove("copy");
1228   - }
1229   -
1230   - // }}}
1231   -
1232   - // {{{ http_MOVE()
1233   -
1234   - /**
1235   - * MOVE method handler
1236   - *
1237   - * @param void
1238   - * @return void
1239   - */
1240   - function http_MOVE()
1241   - {
1242   - if ($this->_check_lock_status($this->path)) {
1243   - // destination lock status is always checked by the helper method
1244   - $this->_copymove("move");
1245   - } else {
1246   - $this->http_status("423 Locked");
1247   - }
1248   - }
1249   -
1250   - // }}}
1251   -
1252   -
1253   - // {{{ http_LOCK()
1254   -
1255   - /**
1256   - * LOCK method handler
1257   - *
1258   - * @param void
1259   - * @return void
1260   - */
1261   - function http_LOCK()
1262   - {
1263   - $options = Array();
1264   - $options["path"] = $this->path;
1265   -
1266   - if (isset($_SERVER['HTTP_DEPTH'])) {
1267   - $options["depth"] = $_SERVER["HTTP_DEPTH"];
1268   - } else {
1269   - $options["depth"] = "infinity";
1270   - }
1271   -
1272   - if (isset($_SERVER["HTTP_TIMEOUT"])) {
1273   - $options["timeout"] = explode(",", $_SERVER["HTTP_TIMEOUT"]);
1274   - }
1275   -
1276   - if(empty($_SERVER['CONTENT_LENGTH']) && !empty($_SERVER['HTTP_IF'])) {
1277   - // check if locking is possible
1278   - if(!$this->_check_lock_status($this->path)) {
1279   - $this->http_status("423 Locked");
1280   - return;
1281   - }
1282   -
1283   - // refresh lock
1284   - $options["update"] = substr($_SERVER['HTTP_IF'], 2, -2);
1285   - $stat = $this->LOCK($options);
1286   - } else {
1287   - // extract lock request information from request XML payload
1288   - $lockinfo = new _parse_lockinfo("php://input");
1289   - if (!$lockinfo->success) {
1290   - $this->http_status("400 bad request");
1291   - }
1292   -
1293   - // check if locking is possible
1294   - if(!$this->_check_lock_status($this->path, $lockinfo->lockscope === "shared")) {
1295   - $this->http_status("423 Locked");
1296   - return;
1297   - }
1298   -
1299   - // new lock
1300   - $options["scope"] = $lockinfo->lockscope;
1301   - $options["type"] = $lockinfo->locktype;
1302   - $options["owner"] = $lockinfo->owner;
1303   -
1304   - $options["locktoken"] = $this->_new_locktoken();
1305   -
1306   - $stat = $this->LOCK($options);
1307   - }
1308   -
1309   - if(is_bool($stat)) {
1310   - $http_stat = $stat ? "200 OK" : "423 Locked";
1311   - } else {
1312   - $http_stat = $stat;
1313   - }
1314   -
1315   - $this->http_status($http_stat);
1316   -
1317   - if ($http_stat{0} == 2) { // 2xx states are ok
1318   - if($options["timeout"]) {
1319   - // more than a million is considered an absolute timestamp
1320   - // less is more likely a relative value
1321   - if($options["timeout"]>1000000) {
1322   - $timeout = "Second-".($options['timeout']-time());
1323   - } else {
1324   - $timeout = "Second-$options[timeout]";
1325   - }
1326   - } else {
1327   - $timeout = "Infinite";
1328   - }
1329   -
1330   - header('Content-Type: text/xml; charset="utf-8"');
1331   - header("Lock-Token: <$options[locktoken]>");
1332   - echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1333   - echo "<D:prop xmlns:D=\"DAV:\">\n";
1334   - echo " <D:lockdiscovery>\n";
1335   - echo " <D:activelock>\n";
1336   - echo " <D:lockscope><D:$options[scope]/></D:lockscope>\n";
1337   - echo " <D:locktype><D:$options[type]/></D:locktype>\n";
1338   - echo " <D:depth>$options[depth]</D:depth>\n";
1339   - echo " <D:owner>$options[owner]</D:owner>\n";
1340   - echo " <D:timeout>$timeout</D:timeout>\n";
1341   - echo " <D:locktoken><D:href>$options[locktoken]</D:href></D:locktoken>\n";
1342   - echo " </D:activelock>\n";
1343   - echo " </D:lockdiscovery>\n";
1344   - echo "</D:prop>\n\n";
1345   - }
1346   - }
1347   -
1348   -
1349   - // }}}
1350   -
1351   - // {{{ http_UNLOCK()
1352   -
1353   - /**
1354   - * UNLOCK method handler
1355   - *
1356   - * @param void
1357   - * @return void
1358   - */
1359   - function http_UNLOCK()
1360   - {
1361   - $options = Array();
1362   - $options["path"] = $this->path;
1363   -
1364   - if (isset($_SERVER['HTTP_DEPTH'])) {
1365   - $options["depth"] = $_SERVER["HTTP_DEPTH"];
1366   - } else {
1367   - $options["depth"] = "infinity";
1368   - }
1369   -
1370   - // strip surrounding <>
1371   - $options["token"] = substr(trim($_SERVER["HTTP_LOCK_TOKEN"]), 1, -1);
1372   -
1373   - // call user method
1374   - $stat = $this->UNLOCK($options);
1375   -
1376   - $this->http_status($stat);
1377   - }
1378   -
1379   - // }}}
1380   -
1381   - // }}}
1382   -
1383   - // {{{ _copymove()
1384   -
1385   - function _copymove($what)
1386   - {
1387   - $options = Array();
1388   - $options["path"] = $this->path;
1389   -
1390   - if (isset($_SERVER["HTTP_DEPTH"])) {
1391   - $options["depth"] = $_SERVER["HTTP_DEPTH"];
1392   - } else {
1393   - $options["depth"] = "infinity";
1394   - }
1395   -
1396   - extract(parse_url($_SERVER["HTTP_DESTINATION"]));
1397   - $path = urldecode($path);
1398   - $http_host = $host;
1399   - if (isset($port) && $port != 80)
1400   - $http_host.= ":$port";
1401   -
1402   - $http_header_host = ereg_replace(":80$", "", $_SERVER["HTTP_HOST"]);
1403   -
1404   - if ($http_host == $http_header_host &&
1405   - !strncmp($_SERVER["SCRIPT_NAME"], $path,
1406   - strlen($_SERVER["SCRIPT_NAME"]))) {
1407   - $options["dest"] = substr($path, strlen($_SERVER["SCRIPT_NAME"]));
1408   - if (!$this->_check_lock_status($options["dest"])) {
1409   - $this->http_status("423 Locked");
1410   - return;
1411   - }
1412   -
1413   - } else {
1414   - $options["dest_url"] = $_SERVER["HTTP_DESTINATION"];
1415   - }
1416   -
1417   - // see RFC 2518 Sections 9.6, 8.8.4 and 8.9.3
1418   - if (isset($_SERVER["HTTP_OVERWRITE"])) {
1419   - $options["overwrite"] = $_SERVER["HTTP_OVERWRITE"] == "T";
1420   - } else {
1421   - $options["overwrite"] = true;
1422   - }
1423   -
1424   - $stat = $this->$what($options);
1425   - $this->http_status($stat);
1426   - }
1427   -
1428   - // }}}
1429   -
1430   - // {{{ _allow()
1431   -
1432   - /**
1433   - * check for implemented HTTP methods
1434   - *
1435   - * @param void
1436   - * @return array something
1437   - */
1438   - function _allow()
1439   - {
1440   - // OPTIONS is always there
1441   - $allow = array("OPTIONS" =>"OPTIONS");
1442   -
1443   - // all other METHODS need both a http_method() wrapper
1444   - // and a method() implementation
1445   - // the base class supplies wrappers only
1446   - foreach(get_class_methods($this) as $method) {
1447   - if (!strncmp("http_", $method, 5)) {
1448   - $method = strtoupper(substr($method, 5));
1449   - if (method_exists($this, $method)) {
1450   - $allow[$method] = $method;
1451   - }
1452   - }
1453   - }
1454   -
1455   - // we can emulate a missing HEAD implemetation using GET
1456   - if (isset($allow["GET"]))
1457   - $allow["HEAD"] = "HEAD";
1458   -
1459   - // no LOCK without checklok()
1460   - if (!method_exists($this, "checklock")) {
1461   - unset($allow["LOCK"]);
1462   - unset($allow["UNLOCK"]);
1463   - }
1464   -
1465   - return $allow;
1466   - }
1467   -
1468   - // }}}
1469   -
1470   - /**
1471   - * helper for property element creation
1472   - *
1473   - * @param string XML namespace (optional)
1474   - * @param string property name
1475   - * @param string property value
1476   - * @return array property array
1477   - */
1478   - function mkprop()
1479   - {
1480   - $args = func_get_args();
1481   - if (count($args) == 3) {
1482   - return array("ns" => $args[0],
1483   - "name" => $args[1],
1484   - "val" => $args[2]);
1485   - } else {
1486   - return array("ns" => "DAV:",
1487   - "name" => $args[0],
1488   - "val" => $args[1]);
1489   - }
1490   - }
1491   -
1492   - // {{{ _check_auth
1493   -
1494   - /**
1495   - * check authentication if check is implemented
1496   - *
1497   - * @param void
1498   - * @return bool true if authentication succeded or not necessary
1499   - */
1500   - function _check_auth()
1501   - {
1502   - if (method_exists($this, "checkAuth")) {
1503   - // PEAR style method name
1504   - return $this->checkAuth(@$_SERVER["AUTH_TYPE"],
1505   - @$_SERVER["PHP_AUTH_USER"],
1506   - @$_SERVER["PHP_AUTH_PW"]);
1507   - } else if (method_exists($this, "check_auth")) {
1508   - // old (pre 1.0) method name
1509   - return $this->check_auth(@$_SERVER["AUTH_TYPE"],
1510   - @$_SERVER["PHP_AUTH_USER"],
1511   - @$_SERVER["PHP_AUTH_PW"]);
1512   - } else {
1513   - // no method found -> no authentication required
1514   - return true;
1515   - }
1516   - }
1517   -
1518   - // }}}
1519   -
1520   - // {{{ UUID stuff
1521   -
1522   - /**
1523   - * generate Unique Universal IDentifier for lock token
1524   - *
1525   - * @param void
1526   - * @return string a new UUID
1527   - */
1528   - function _new_uuid()
1529   - {
1530   - // use uuid extension from PECL if available
1531   - if (function_exists("uuid_create")) {
1532   - return uuid_create();
1533   - }
1534   -
1535   - // fallback
1536   - $uuid = md5(microtime().getmypid()); // this should be random enough for now
1537   -
1538   - // set variant and version fields for 'true' random uuid
1539   - $uuid{12} = "4";
1540   - $n = 8 + (ord($uuid{16}) & 3);
1541   - $hex = "0123456789abcdef";
1542   - $uuid{16} = $hex{$n};
1543   -
1544   - // return formated uuid
1545   - return substr($uuid, 0, 8)."-"
1546   - . substr($uuid, 8, 4)."-"
1547   - . substr($uuid, 12, 4)."-"
1548   - . substr($uuid, 16, 4)."-"
1549   - . substr($uuid, 20);
1550   - }
1551   -
1552   - /**
1553   - * create a new opaque lock token as defined in RFC2518
1554   - *
1555   - * @param void
1556   - * @return string new RFC2518 opaque lock token
1557   - */
1558   - function _new_locktoken()
1559   - {
1560   - return "opaquelocktoken:".$this->_new_uuid();
1561   - }
1562   -
1563   - // }}}
1564   -
1565   - // {{{ WebDAV If: header parsing
1566   -
1567   - /**
1568   - *
1569   - *
1570   - * @param string header string to parse
1571   - * @param int current parsing position
1572   - * @return array next token (type and value)
1573   - */
1574   - function _if_header_lexer($string, &$pos)
1575   - {
1576   - // skip whitespace
1577   - while (ctype_space($string{$pos})) {
1578   - ++$pos;
1579   - }
1580   -
1581   - // already at end of string?
1582   - if (strlen($string) <= $pos) {
1583   - return false;
1584   - }
1585   -
1586   - // get next character
1587   - $c = $string{$pos++};
1588   -
1589   - // now it depends on what we found
1590   - switch ($c) {
1591   - case "<":
1592   - // URIs are enclosed in <...>
1593   - $pos2 = strpos($string, ">", $pos);
1594   - $uri = substr($string, $pos, $pos2 - $pos);
1595   - $pos = $pos2 + 1;
1596   - return array("URI", $uri);
1597   -
1598   - case "[":
1599   - //Etags are enclosed in [...]
1600   - if ($string{$pos} == "W") {
1601   - $type = "ETAG_WEAK";
1602   - $pos += 2;
1603   - } else {
1604   - $type = "ETAG_STRONG";
1605   - }
1606   - $pos2 = strpos($string, "]", $pos);
1607   - $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
1608   - $pos = $pos2 + 1;
1609   - return array($type, $etag);
1610   -
1611   - case "N":
1612   - // "N" indicates negation
1613   - $pos += 2;
1614   - return array("NOT", "Not");
1615   -
1616   - default:
1617   - // anything else is passed verbatim char by char
1618   - return array("CHAR", $c);
1619   - }
1620   - }
1621   -
1622   - /**
1623   - * parse If: header
1624   - *
1625   - * @param string header string
1626   - * @return array URIs and their conditions
1627   - */
1628   - function _if_header_parser($str)
1629   - {
1630   - $pos = 0;
1631   - $len = strlen($str);
1632   -
1633   - $uris = array();
1634   -
1635   - // parser loop
1636   - while ($pos < $len) {
1637   - // get next token
1638   - $token = $this->_if_header_lexer($str, $pos);
1639   -
1640   - // check for URI
1641   - if ($token[0] == "URI") {
1642   - $uri = $token[1]; // remember URI
1643   - $token = $this->_if_header_lexer($str, $pos); // get next token
1644   - } else {
1645   - $uri = "";
1646   - }
1647   -
1648   - // sanity check
1649   - if ($token[0] != "CHAR" || $token[1] != "(") {
1650   - return false;
1651   - }
1652   -
1653   - $list = array();
1654   - $level = 1;
1655   - $not = "";
1656   - while ($level) {
1657   - $token = $this->_if_header_lexer($str, $pos);
1658   - if ($token[0] == "NOT") {
1659   - $not = "!";
1660   - continue;
1661   - }
1662   - switch ($token[0]) {
1663   - case "CHAR":
1664   - switch ($token[1]) {
1665   - case "(":
1666   - $level++;
1667   - break;
1668   - case ")":
1669   - $level--;
1670   - break;
1671   - default:
1672   - return false;
1673   - }
1674   - break;
1675   -
1676   - case "URI":
1677   - $list[] = $not."<$token[1]>";
1678   - break;
1679   -
1680   - case "ETAG_WEAK":
1681   - $list[] = $not."[W/'$token[1]']>";
1682   - break;
1683   -
1684   - case "ETAG_STRONG":
1685   - $list[] = $not."['$token[1]']>";
1686   - break;
1687   -
1688   - default:
1689   - return false;
1690   - }
1691   - $not = "";
1692   - }
1693   -
1694   - if (@is_array($uris[$uri])) {
1695   - $uris[$uri] = array_merge($uris[$uri],$list);
1696   - } else {
1697   - $uris[$uri] = $list;
1698   - }
1699   - }
1700   -
1701   - return $uris;
1702   - }
1703   -
1704   - /**
1705   - * check if conditions from "If:" headers are meat
1706   - *
1707   - * the "If:" header is an extension to HTTP/1.1
1708   - * defined in RFC 2518 section 9.4
1709   - *
1710   - * @param void
1711   - * @return void
1712   - */
1713   - function _check_if_header_conditions()
1714   - {
1715   - if (isset($_SERVER["HTTP_IF"])) {
1716   - $this->_if_header_uris =
1717   - $this->_if_header_parser($_SERVER["HTTP_IF"]);
1718   -
1719   - foreach($this->_if_header_uris as $uri => $conditions) {
1720   - if ($uri == "") {
1721   - $uri = $this->uri;
1722   - }
1723   - // all must match
1724   - $state = true;
1725   - foreach($conditions as $condition) {
1726   - // lock tokens may be free form (RFC2518 6.3)
1727   - // but if opaquelocktokens are used (RFC2518 6.4)
1728   - // we have to check the format (litmus tests this)
1729   - if (!strncmp($condition, "<opaquelocktoken:", strlen("<opaquelocktoken"))) {
1730   - if (!ereg("^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$", $condition)) {
1731   - return false;
1732   - }
1733   - }
1734   - if (!$this->_check_uri_condition($uri, $condition)) {
1735   - $state = false;
1736   - break;
1737   - }
1738   - }
1739   -
1740   - // any match is ok
1741   - if ($state == true) {
1742   - return true;
1743   - }
1744   - }
1745   - return false;
1746   - }
1747   - return true;
1748   - }
1749   -
1750   - /**
1751   - * Check a single URI condition parsed from an if-header
1752   - *
1753   - * Check a single URI condition parsed from an if-header
1754   - *
1755   - * @abstract
1756   - * @param string $uri URI to check
1757   - * @param string $condition Condition to check for this URI
1758   - * @returns bool Condition check result
1759   - */
1760   - function _check_uri_condition($uri, $condition)
1761   - {
1762   - // not really implemented here,
1763   - // implementations must override
1764   - return true;
1765   - }
1766   -
1767   -
1768   - /**
1769   - *
1770   - *
1771   - * @param string path of resource to check
1772   - * @param bool exclusive lock?
1773   - */
1774   - function _check_lock_status($path, $exclusive_only = false)
1775   - {
1776   - // FIXME depth -> ignored for now
1777   - if (method_exists($this, "checkLock")) {
1778   - // is locked?
1779   - $lock = $this->checkLock($path);
1780   -
1781   - // ... and lock is not owned?
1782   - if (is_array($lock) && count($lock)) {
1783   - // FIXME doesn't check uri restrictions yet
1784   - if (!strstr($_SERVER["HTTP_IF"], $lock["token"])) {
1785   - if (!$exclusive_only || ($lock["scope"] !== "shared"))
1786   - return false;
1787   - }
1788   - }
1789   - }
1790   - return true;
1791   - }
1792   -
1793   -
1794   - // }}}
1795   -
1796   -
1797   - /**
1798   - * Generate lockdiscovery reply from checklock() result
1799   - *
1800   - * @param string resource path to check
1801   - * @return string lockdiscovery response
1802   - */
1803   - function lockdiscovery($path)
1804   - {
1805   - // no lock support without checklock() method
1806   - if (!method_exists($this, "checklock")) {
1807   - return "";
1808   - }
1809   -
1810   - // collect response here
1811   - $activelocks = "";
1812   -
1813   - // get checklock() reply
1814   - $lock = $this->checklock($path);
1815   -
1816   - // generate <activelock> block for returned data
1817   - if (is_array($lock) && count($lock)) {
1818   - // check for 'timeout' or 'expires'
1819   - if (!empty($lock["expires"])) {
1820   - $timeout = "Second-".($lock["expires"] - time());
1821   - } else if (!empty($lock["timeout"])) {
1822   - $timeout = "Second-$lock[timeout]";
1823   - } else {
1824   - $timeout = "Infinite";
1825   - }
1826   -
1827   - // genreate response block
1828   - $activelocks.= "
1829   - <D:activelock>
1830   - <D:lockscope><D:$lock[scope]/></D:lockscope>
1831   - <D:locktype><D:$lock[type]/></D:locktype>
1832   - <D:depth>$lock[depth]</D:depth>
1833   - <D:owner>$lock[owner]</D:owner>
1834   - <D:timeout>$timeout</D:timeout>
1835   - <D:locktoken><D:href>$lock[token]</D:href></D:locktoken>
1836   - </D:activelock>
1837   - ";
1838   - }
1839   -
1840   - // return generated response
1841   - return $activelocks;
1842   - }
1843   -
1844   - /**
1845   - * set HTTP return status and mirror it in a private header
1846   - *
1847   - * @param string status code and message
1848   - * @return void
1849   - */
1850   - function http_status($status)
1851   - {
1852   - // simplified success case
1853   - if($status === true) {
1854   - $status = "200 OK";
1855   - }
1856   -
1857   - // remember status
1858   - $this->_http_status = $status;
1859   -
1860   - // generate HTTP status response
1861   - header("HTTP/1.1 $status");
1862   - header("X-WebDAV-Status: $status", true);
1863   - }
1864   -
1865   - /**
1866   - * private minimalistic version of PHP urlencode()
1867   - *
1868   - * only blanks and XML special chars must be encoded here
1869   - * full urlencode() encoding confuses some clients ...
1870   - *
1871   - * @param string URL to encode
1872   - * @return string encoded URL
1873   - */
1874   - function _urlencode($url)
1875   - {
1876   - return strtr($url, array(" "=>"%20",
1877   - "&"=>"%26",
1878   - "<"=>"%3C",
1879   - ">"=>"%3E",
1880   - ));
1881   - }
1882   -
1883   - /**
1884   - * private version of PHP urldecode
1885   - *
1886   - * not really needed but added for completenes
1887   - *
1888   - * @param string URL to decode
1889   - * @return string decoded URL
1890   - */
1891   - function _urldecode($path)
1892   - {
1893   - return urldecode($path);
1894   - }
1895   -
1896   - /**
1897   - * UTF-8 encode property values if not already done so
1898   - *
1899   - * @param string text to encode
1900   - * @return string utf-8 encoded text
1901   - */
1902   - function _prop_encode($text)
1903   - {
1904   - switch (strtolower($this->_prop_encoding)) {
1905   - case "utf-8":
1906   - return $text;
1907   - case "iso-8859-1":
1908   - case "iso-8859-15":
1909   - case "latin-1":
1910   - default:
1911   - return utf8_encode($text);
1912   - }
1913   - }
1914   -
1915   - /**
1916   - * Slashify - make sure path ends in a slash
1917   - *
1918   - * @param string directory path
1919   - * @returns string directory path wiht trailing slash
1920   - */
1921   - function _slashify($path) {
1922   - if ($path[strlen($path)-1] != '/') {
1923   - $path = $path."/";
1924   - }
1925   - return $path;
1926   - }
1927   -
1928   - /**
1929   - * Unslashify - make sure path doesn't in a slash
1930   - *
1931   - * @param string directory path
1932   - * @returns string directory path wihtout trailing slash
1933   - */
1934   - function _unslashify($path) {
1935   - if ($path[strlen($path)-1] == '/') {
1936   - $path = substr($path, 0, strlen($path, 0, -1));
1937   - }
1938   - return $path;
1939   - }
1940   -
1941   - /**
1942   - * Merge two pathes, make sure there is exactly one slash between them
1943   - *
1944   - * @param string parent path
1945   - * @param string child path
1946   - * @return string merged path
1947   - */
1948   - function _mergePathes($parent, $child)
1949   - {
1950   - if ($child{0} == '/') {
1951   - return $this->_unslashify($parent).$child;
1952   - } else {
1953   - return $this->_slashify($parent).$child;
1954   - }
1955   - }
1956   -}
1957   -
1958   - /*
1959   - * Local variables:
1960   - * tab-width: 4
1961   - * c-basic-offset: 4
1962   - * End:
1963   - */
1964   -?>
thirdparty/pear/HTTP/WebDAV/Tools/_parse_lockinfo.php deleted
1   -<?php
2   -//
3   -// +----------------------------------------------------------------------+
4   -// | PHP Version 4 |
5   -// +----------------------------------------------------------------------+
6   -// | Copyright (c) 1997-2003 The PHP Group |
7   -// +----------------------------------------------------------------------+
8   -// | This source file is subject to version 2.02 of the PHP license, |
9   -// | that is bundled with this package in the file LICENSE, and is |
10   -// | available at through the world-wide-web at |
11   -// | http://www.php.net/license/2_02.txt. |
12   -// | If you did not receive a copy of the PHP license and are unable to |
13   -// | obtain it through the world-wide-web, please send a note to |
14   -// | license@php.net so we can mail you a copy immediately. |
15   -// +----------------------------------------------------------------------+
16   -// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
17   -// | Christian Stocker <chregu@bitflux.ch> |
18   -// +----------------------------------------------------------------------+
19   -//
20   -// $Id$
21   -//
22   -
23   -/**
24   - * helper class for parsing LOCK request bodies
25   - *
26   - * @package HTTP_WebDAV_Server
27   - * @author Hartmut Holzgraefe <hholzgra@php.net>
28   - * @version 0.99.1dev
29   - */
30   -class _parse_lockinfo
31   -{
32   - /**
33   - * success state flag
34   - *
35   - * @var bool
36   - * @access public
37   - */
38   - var $success = false;
39   -
40   - /**
41   - * lock type, currently only "write"
42   - *
43   - * @var string
44   - * @access public
45   - */
46   - var $locktype = "";
47   -
48   - /**
49   - * lock scope, "shared" or "exclusive"
50   - *
51   - * @var string
52   - * @access public
53   - */
54   - var $lockscope = "";
55   -
56   - /**
57   - * lock owner information
58   - *
59   - * @var string
60   - * @access public
61   - */
62   - var $owner = "";
63   -
64   - /**
65   - * flag that is set during lock owner read
66   - *
67   - * @var bool
68   - * @access private
69   - */
70   - var $collect_owner = false;
71   -
72   - /**
73   - * constructor
74   - *
75   - * @param string path of stream to read
76   - * @access public
77   - */
78   - function _parse_lockinfo($path)
79   - {
80   - // we assume success unless problems occur
81   - $this->success = true;
82   -
83   - // remember if any input was parsed
84   - $had_input = false;
85   -
86   - // open stream
87   - $f_in = fopen($path, "r");
88   - if (!$f_in) {
89   - $this->success = false;
90   - return;
91   - }
92   -
93   - // create namespace aware parser
94   - $xml_parser = xml_parser_create_ns("UTF-8", " ");
95   -
96   - // set tag and data handlers
97   - xml_set_element_handler($xml_parser,
98   - array(&$this, "_startElement"),
99   - array(&$this, "_endElement"));
100   - xml_set_character_data_handler($xml_parser,
101   - array(&$this, "_data"));
102   -
103   - // we want a case sensitive parser
104   - xml_parser_set_option($xml_parser,
105   - XML_OPTION_CASE_FOLDING, false);
106   -
107   - // parse input
108   - while($this->success && !feof($f_in)) {
109   - $line = fgets($f_in);
110   - if (is_string($line)) {
111   - $had_input = true;
112   - $this->success &= xml_parse($xml_parser, $line, false);
113   - }
114   - }
115   -
116   - // finish parsing
117   - if($had_input) {
118   - $this->success &= xml_parse($xml_parser, "", true);
119   - }
120   -
121   - // check if required tags where found
122   - $this->success &= !empty($this->locktype);
123   - $this->success &= !empty($this->lockscope);
124   -
125   - // free parser resource
126   - xml_parser_free($xml_parser);
127   -
128   - // close input stream
129   - fclose($f_in);
130   - }
131   -
132   -
133   - /**
134   - * tag start handler
135   - *
136   - * @param resource parser
137   - * @param string tag name
138   - * @param array tag attributes
139   - * @return void
140   - * @access private
141   - */
142   - function _startElement($parser, $name, $attrs)
143   - {
144   - // namespace handling
145   - if (strstr($name, " ")) {
146   - list($ns, $tag) = explode(" ", $name);
147   - } else {
148   - $ns = "";
149   - $tag = $name;
150   - }
151   -
152   -
153   - if ($this->collect_owner) {
154   - // everything within the <owner> tag needs to be collected
155   - $ns_short = "";
156   - $ns_attr = "";
157   - if ($ns) {
158   - if ($ns == "DAV:") {
159   - $ns_short = "D:";
160   - } else {
161   - $ns_attr = " xmlns='$ns'";
162   - }
163   - }
164   - $this->owner .= "<$ns_short$tag$ns_attr>";
165   - } else if ($ns == "DAV:") {
166   - // parse only the essential tags
167   - switch ($tag) {
168   - case "write":
169   - $this->locktype = $tag;
170   - break;
171   - case "exclusive":
172   - case "shared":
173   - $this->lockscope = $tag;
174   - break;
175   - case "owner":
176   - $this->collect_owner = true;
177   - break;
178   - }
179   - }
180   - }
181   -
182   - /**
183   - * data handler
184   - *
185   - * @param resource parser
186   - * @param string data
187   - * @return void
188   - * @access private
189   - */
190   - function _data($parser, $data)
191   - {
192   - // only the <owner> tag has data content
193   - if ($this->collect_owner) {
194   - $this->owner .= $data;
195   - }
196   - }
197   -
198   - /**
199   - * tag end handler
200   - *
201   - * @param resource parser
202   - * @param string tag name
203   - * @return void
204   - * @access private
205   - */
206   - function _endElement($parser, $name)
207   - {
208   - // namespace handling
209   - if (strstr($name, " ")) {
210   - list($ns, $tag) = explode(" ", $name);
211   - } else {
212   - $ns = "";
213   - $tag = $name;
214   - }
215   -
216   - // <owner> finished?
217   - if (($ns == "DAV:") && ($tag == "owner")) {
218   - $this->collect_owner = false;
219   - }
220   -
221   - // within <owner> we have to collect everything
222   - if ($this->collect_owner) {
223   - $ns_short = "";
224   - $ns_attr = "";
225   - if ($ns) {
226   - if ($ns == "DAV:") {
227   - $ns_short = "D:";
228   - } else {
229   - $ns_attr = " xmlns='$ns'";
230   - }
231   - }
232   - $this->owner .= "</$ns_short$tag$ns_attr>";
233   - }
234   - }
235   -}
236   -
237   -?>
238 0 \ No newline at end of file
thirdparty/pear/HTTP/WebDAV/Tools/_parse_propfind.php deleted
1   -<?php
2   -//
3   -// +----------------------------------------------------------------------+
4   -// | PHP Version 4 |
5   -// +----------------------------------------------------------------------+
6   -// | Copyright (c) 1997-2003 The PHP Group |
7   -// +----------------------------------------------------------------------+
8   -// | This source file is subject to version 2.02 of the PHP license, |
9   -// | that is bundled with this package in the file LICENSE, and is |
10   -// | available at through the world-wide-web at |
11   -// | http://www.php.net/license/2_02.txt. |
12   -// | If you did not receive a copy of the PHP license and are unable to |
13   -// | obtain it through the world-wide-web, please send a note to |
14   -// | license@php.net so we can mail you a copy immediately. |
15   -// +----------------------------------------------------------------------+
16   -// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
17   -// | Christian Stocker <chregu@bitflux.ch> |
18   -// +----------------------------------------------------------------------+
19   -//
20   -// $Id$
21   -//
22   -
23   -/**
24   - * helper class for parsing PROPFIND request bodies
25   - *
26   - * @package HTTP_WebDAV_Server
27   - * @author Hartmut Holzgraefe <hholzgra@php.net>
28   - * @version 0.99.1dev
29   - */
30   -class _parse_propfind
31   -{
32   - /**
33   - * success state flag
34   - *
35   - * @var bool
36   - * @access public
37   - */
38   - var $success = false;
39   -
40   - /**
41   - * found properties are collected here
42   - *
43   - * @var array
44   - * @access public
45   - */
46   - var $props = false;
47   -
48   - /**
49   - * internal tag nesting depth counter
50   - *
51   - * @var int
52   - * @access private
53   - */
54   - var $depth = 0;
55   -
56   -
57   - /**
58   - * constructor
59   - *
60   - * @access public
61   - */
62   - function _parse_propfind($path)
63   - {
64   - // success state flag
65   - $this->success = true;
66   -
67   - // property storage array
68   - $this->props = array();
69   -
70   - // internal tag depth counter
71   - $this->depth = 0;
72   -
73   - // remember if any input was parsed
74   - $had_input = false;
75   -
76   - // open input stream
77   - $f_in = fopen($path, "r");
78   - if (!$f_in) {
79   - $this->success = false;
80   - return;
81   - }
82   -
83   - // create XML parser
84   - $xml_parser = xml_parser_create_ns("UTF-8", " ");
85   -
86   - // set tag and data handlers
87   - xml_set_element_handler($xml_parser,
88   - array(&$this, "_startElement"),
89   - array(&$this, "_endElement"));
90   -
91   - // we want a case sensitive parser
92   - xml_parser_set_option($xml_parser,
93   - XML_OPTION_CASE_FOLDING, false);
94   -
95   -
96   - // parse input
97   - while($this->success && !feof($f_in)) {
98   - $line = fgets($f_in);
99   - if (is_string($line)) {
100   - $had_input = true;
101   - $this->success &= xml_parse($xml_parser, $line, false);
102   - }
103   - }
104   -
105   - // finish parsing
106   - if($had_input) {
107   - $this->success &= xml_parse($xml_parser, "", true);
108   - }
109   -
110   - // free parser
111   - xml_parser_free($xml_parser);
112   -
113   - // close input stream
114   - fclose($f_in);
115   -
116   - // if no input was parsed it was a request
117   - if(!count($this->props)) $this->props = "all"; // default
118   - }
119   -
120   -
121   - /**
122   - * start tag handler
123   - *
124   - * @access private
125   - * @param resource parser
126   - * @param string tag name
127   - * @param array tag attributes
128   - */
129   - function _startElement($parser, $name, $attrs)
130   - {
131   - // name space handling
132   - if (strstr($name, " ")) {
133   - list($ns, $tag) = explode(" ", $name);
134   - if ($ns == "")
135   - $this->success = false;
136   - } else {
137   - $ns = "";
138   - $tag = $name;
139   - }
140   -
141   - // special tags at level 1: <allprop> and <propname>
142   - if ($this->depth == 1) {
143   - if ($tag == "allprop")
144   - $this->props = "all";
145   -
146   - if ($tag == "propname")
147   - $this->props = "names";
148   - }
149   -
150   - // requested properties are found at level 2
151   - if ($this->depth == 2) {
152   - $prop = array("name" => $tag);
153   - if ($ns)
154   - $prop["xmlns"] = $ns;
155   - $this->props[] = $prop;
156   - }
157   -
158   - // increment depth count
159   - $this->depth++;
160   - }
161   -
162   -
163   - /**
164   - * end tag handler
165   - *
166   - * @access private
167   - * @param resource parser
168   - * @param string tag name
169   - */
170   - function _endElement($parser, $name)
171   - {
172   - // here we only need to decrement the depth count
173   - $this->depth--;
174   - }
175   -}
176   -
177   -
178   -?>
179 0 \ No newline at end of file
thirdparty/pear/HTTP/WebDAV/Tools/_parse_proppatch.php deleted
1   -<?php
2   -//
3   -// +----------------------------------------------------------------------+
4   -// | PHP Version 4 |
5   -// +----------------------------------------------------------------------+
6   -// | Copyright (c) 1997-2003 The PHP Group |
7   -// +----------------------------------------------------------------------+
8   -// | This source file is subject to version 2.02 of the PHP license, |
9   -// | that is bundled with this package in the file LICENSE, and is |
10   -// | available at through the world-wide-web at |
11   -// | http://www.php.net/license/2_02.txt. |
12   -// | If you did not receive a copy of the PHP license and are unable to |
13   -// | obtain it through the world-wide-web, please send a note to |
14   -// | license@php.net so we can mail you a copy immediately. |
15   -// +----------------------------------------------------------------------+
16   -// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
17   -// | Christian Stocker <chregu@bitflux.ch> |
18   -// +----------------------------------------------------------------------+
19   -//
20   -// $Id$
21   -//
22   -
23   -/**
24   - * helper class for parsing PROPPATCH request bodies
25   - *
26   - * @package HTTP_WebDAV_Server
27   - * @author Hartmut Holzgraefe <hholzgra@php.net>
28   - * @version 0.99.1dev
29   - */
30   -class _parse_proppatch
31   -{
32   - /**
33   - *
34   - *
35   - * @var
36   - * @access
37   - */
38   - var $success;
39   -
40   - /**
41   - *
42   - *
43   - * @var
44   - * @access
45   - */
46   - var $props;
47   -
48   - /**
49   - *
50   - *
51   - * @var
52   - * @access
53   - */
54   - var $depth;
55   -
56   - /**
57   - *
58   - *
59   - * @var
60   - * @access
61   - */
62   - var $mode;
63   -
64   - /**
65   - *
66   - *
67   - * @var
68   - * @access
69   - */
70   - var $current;
71   -
72   - /**
73   - * constructor
74   - *
75   - * @param string path of input stream
76   - * @access public
77   - */
78   - function _parse_proppatch($path)
79   - {
80   - $this->success = true;
81   -
82   - $this->depth = 0;
83   - $this->props = array();
84   - $had_input = false;
85   -
86   - $f_in = fopen($path, "r");
87   - if (!$f_in) {
88   - $this->success = false;
89   - return;
90   - }
91   -
92   - $xml_parser = xml_parser_create_ns("UTF-8", " ");
93   -
94   - xml_set_element_handler($xml_parser,
95   - array(&$this, "_startElement"),
96   - array(&$this, "_endElement"));
97   -
98   - xml_set_character_data_handler($xml_parser,
99   - array(&$this, "_data"));
100   -
101   - xml_parser_set_option($xml_parser,
102   - XML_OPTION_CASE_FOLDING, false);
103   -
104   - while($this->success && !feof($f_in)) {
105   - $line = fgets($f_in);
106   - if (is_string($line)) {
107   - $had_input = true;
108   - $this->success &= xml_parse($xml_parser, $line, false);
109   - }
110   - }
111   -
112   - if($had_input) {
113   - $this->success &= xml_parse($xml_parser, "", true);
114   - }
115   -
116   - xml_parser_free($xml_parser);
117   -
118   - fclose($f_in);
119   - }
120   -
121   - /**
122   - * tag start handler
123   - *
124   - * @param resource parser
125   - * @param string tag name
126   - * @param array tag attributes
127   - * @return void
128   - * @access private
129   - */
130   - function _startElement($parser, $name, $attrs)
131   - {
132   - if (strstr($name, " ")) {
133   - list($ns, $tag) = explode(" ", $name);
134   - if ($ns == "")
135   - $this->success = false;
136   - } else {
137   - $ns = "";
138   - $tag = $name;
139   - }
140   -
141   - if ($this->depth == 1) {
142   - $this->mode = $tag;
143   - }
144   -
145   - if ($this->depth == 3) {
146   - $prop = array("name" => $tag);
147   - $this->current = array("name" => $tag, "ns" => $ns, "status"=> 200);
148   - if ($this->mode == "set") {
149   - $this->current["val"] = ""; // default set val
150   - }
151   - }
152   -
153   - if ($this->depth >= 4) {
154   - $this->current["val"] .= "<$tag";
155   - foreach ($attr as $key => $val) {
156   - $this->current["val"] .= ' '.$key.'="'.str_replace('"','&quot;', $val).'"';
157   - }
158   - $this->current["val"] .= ">";
159   - }
160   -
161   -
162   -
163   - $this->depth++;
164   - }
165   -
166   - /**
167   - * tag end handler
168   - *
169   - * @param resource parser
170   - * @param string tag name
171   - * @return void
172   - * @access private
173   - */
174   - function _endElement($parser, $name)
175   - {
176   - if (strstr($name, " ")) {
177   - list($ns, $tag) = explode(" ", $name);
178   - if ($ns == "")
179   - $this->success = false;
180   - } else {
181   - $ns = "";
182   - $tag = $name;
183   - }
184   -
185   - $this->depth--;
186   -
187   - if ($this->depth >= 4) {
188   - $this->current["val"] .= "</$tag>";
189   - }
190   -
191   - if ($this->depth == 3) {
192   - if (isset($this->current)) {
193   - $this->props[] = $this->current;
194   - unset($this->current);
195   - }
196   - }
197   - }
198   -
199   - /**
200   - * input data handler
201   - *
202   - * @param resource parser
203   - * @param string data
204   - * @return void
205   - * @access private
206   - */
207   - function _data($parser, $data) {
208   - if (isset($this->current)) {
209   - $this->current["val"] .= $data;
210   - }
211   - }
212   -}
213   -
214   -?>
215 0 \ No newline at end of file
thirdparty/pear/WebDAV/Client.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4: */
  3 +// +----------------------------------------------------------------------+
  4 +// | PHP version 4 |
  5 +// +----------------------------------------------------------------------+
  6 +// | Copyright (c) 1997-2003 The PHP Group |
  7 +// +----------------------------------------------------------------------+
  8 +// | This source file is subject to version 3.0 of the PHP license, |
  9 +// | that is bundled with this package in the file LICENSE, and is |
  10 +// | available through the world-wide-web at |
  11 +// | http://www.php.net/license/3_0.txt. |
  12 +// | If you did not receive a copy of the PHP license and are unable to |
  13 +// | obtain it through the world-wide-web, please send a note to |
  14 +// | license@php.net so we can mail you a copy immediately. |
  15 +// +----------------------------------------------------------------------+
  16 +// | Author: Alexey Borzov <avb@php.net> |
  17 +// +----------------------------------------------------------------------+
  18 +//
  19 +// $Id: Client.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  20 +
  21 +require_once 'HTTP/Request.php';
  22 +require_once 'HTTP/Client/CookieManager.php';
  23 +
  24 +/**
  25 + * A simple HTTP client class.
  26 + *
  27 + * The class wraps around HTTP_Request providing a higher-level
  28 + * API for performing multiple HTTP requests
  29 + *
  30 + * @package HTTP_Client
  31 + * @author Alexey Borzov <avb@php.net>
  32 + * @version $Revision: 6819 $
  33 + */
  34 +class HTTP_Client
  35 +{
  36 + /**
  37 + * An HTTP_Client_CookieManager instance
  38 + * @var object
  39 + */
  40 + var $_cookieManager;
  41 +
  42 + /**
  43 + * Received HTTP responses
  44 + * @var array
  45 + */
  46 + var $_responses;
  47 +
  48 + /**
  49 + * Default headers to send on every request
  50 + * @var array
  51 + */
  52 + var $_defaultHeaders = array();
  53 +
  54 + /**
  55 + * Default parameters for HTTP_Request's constructor
  56 + * @var array
  57 + */
  58 + var $_defaultRequestParams = array();
  59 +
  60 + /**
  61 + * How many redirects were done
  62 + * @var integer
  63 + */
  64 + var $_redirectCount = 0;
  65 +
  66 + /**
  67 + * Maximum allowed redirects
  68 + * @var integer
  69 + */
  70 + var $_maxRedirects = 5;
  71 +
  72 + /**
  73 + * Listeners attached to the client
  74 + * @var array
  75 + */
  76 + var $_listeners = array();
  77 +
  78 + /**
  79 + * Whether the listener should be propagated to Request objects
  80 + * @var array
  81 + */
  82 + var $_propagate = array();
  83 +
  84 + /**
  85 + * Whether to keep all the responses or just the most recent one
  86 + * @var boolean
  87 + */
  88 + var $_isHistoryEnabled = true;
  89 +
  90 + /**
  91 + * Constructor
  92 + *
  93 + * @access public
  94 + * @param array Parameters to pass to HTTP_Request's constructor
  95 + * @param array Default headers to send on every request
  96 + */
  97 + function HTTP_Client($defaultRequestParams = null, $defaultHeaders = null)
  98 + {
  99 + $this->_cookieManager =& new HTTP_Client_CookieManager();
  100 + if (isset($defaultHeaders)) {
  101 + $this->setDefaultHeader($defaultHeaders);
  102 + }
  103 + if (isset($defaultRequestParams)) {
  104 + $this->setRequestParameter($defaultRequestParams);
  105 + }
  106 + }
  107 +
  108 +
  109 + /**
  110 + * Sets the maximum redirects that will be processed.
  111 + *
  112 + * Setting this to 0 disables redirect processing. If not 0 and the
  113 + * number of redirects in a request is bigger than this number, then an
  114 + * error will be raised.
  115 + *
  116 + * @access public
  117 + * @param int Max number of redirects to process
  118 + */
  119 + function setMaxRedirects($value)
  120 + {
  121 + $this->_maxRedirects = $value;
  122 + }
  123 +
  124 +
  125 + /**
  126 + * Sets whether to keep all the responses or just the most recent one
  127 + *
  128 + * @access public
  129 + * @param bool Whether to enable history
  130 + */
  131 + function enableHistory($enable)
  132 + {
  133 + $this->_isHistoryEnabled = (bool)$enable;
  134 + }
  135 +
  136 + /**
  137 + * Creates a HTTP_Request objects, applying all the necessary defaults
  138 + *
  139 + * @param string URL
  140 + * @param integer Method, constants are defined in HTTP_Request
  141 + * @access private
  142 + * @return object HTTP_Request object with all defaults applied
  143 + */
  144 + function &_createRequest($url, $method = HTTP_REQUEST_METHOD_GET)
  145 + {
  146 + $req =& new HTTP_Request($url, $this->_defaultRequestParams);
  147 + $req->setMethod($method);
  148 + foreach ($this->_defaultHeaders as $name => $value) {
  149 + $req->addHeader($name, $value);
  150 + }
  151 + $this->_cookieManager->passCookies($req);
  152 + foreach ($this->_propagate as $id => $propagate) {
  153 + if ($propagate) {
  154 + $req->attach($this->_listeners[$id]);
  155 + }
  156 + }
  157 + return $req;
  158 + }
  159 +
  160 +
  161 + /**
  162 + * Sends a 'HEAD' HTTP request
  163 + *
  164 + * @param string URL
  165 + * @access public
  166 + * @return integer HTTP response code
  167 + * @throws PEAR_Error
  168 + */
  169 + function head($url)
  170 + {
  171 + $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_HEAD);
  172 + return $this->_performRequest($request);
  173 + }
  174 +
  175 +
  176 + /**
  177 + * Sends a 'GET' HTTP request
  178 + *
  179 + * @param string URL
  180 + * @param mixed additional data to send
  181 + * @param boolean Whether the data is already urlencoded
  182 + * @access public
  183 + * @return integer HTTP response code
  184 + * @throws PEAR_Error
  185 + */
  186 + function get($url, $data = null, $preEncoded = false)
  187 + {
  188 + $request =& $this->_createRequest($url);
  189 + if (is_array($data)) {
  190 + foreach ($data as $name => $value) {
  191 + $request->addQueryString($name, $value, $preEncoded);
  192 + }
  193 + } elseif (isset($data)) {
  194 + $request->addRawQueryString($data, $preEncoded);
  195 + }
  196 + return $this->_performRequest($request);
  197 + }
  198 +
  199 +
  200 + /**
  201 + * Sends a 'POST' HTTP request
  202 + *
  203 + * @param string URL
  204 + * @param mixed Data to send
  205 + * @param boolean Whether the data is already urlencoded
  206 + * @param array Files to upload. Elements of the array should have the form:
  207 + * array(name, filename(s)[, content type]), see HTTP_Request::addFile()
  208 + * @access public
  209 + * @return integer HTTP response code
  210 + * @throws PEAR_Error
  211 + */
  212 + function post($url, $data, $preEncoded = false, $files = array())
  213 + {
  214 + $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_POST);
  215 + if (is_array($data)) {
  216 + foreach ($data as $name => $value) {
  217 + $request->addPostData($name, $value, $preEncoded);
  218 + }
  219 + } else {
  220 + $request->addRawPostData($data, $preEncoded);
  221 + }
  222 + foreach ($files as $fileData) {
  223 + $res = call_user_func_array(array(&$request, 'addFile'), $fileData);
  224 + if (PEAR::isError($res)) {
  225 + return $res;
  226 + }
  227 + }
  228 + return $this->_performRequest($request);
  229 + }
  230 +
  231 +
  232 + /**
  233 + * Sets default header(s) for HTTP requests
  234 + *
  235 + * @param mixed header name or array ('header name' => 'header value')
  236 + * @param string header value if $name is not an array
  237 + * @access public
  238 + */
  239 + function setDefaultHeader($name, $value = null)
  240 + {
  241 + if (is_array($name)) {
  242 + $this->_defaultHeaders = array_merge($this->_defaultHeaders, $name);
  243 + } else {
  244 + $this->_defaultHeaders[$name] = $value;
  245 + }
  246 + }
  247 +
  248 +
  249 + /**
  250 + * Sets parameter(s) for HTTP requests
  251 + *
  252 + * @param mixed parameter name or array ('parameter name' => 'parameter value')
  253 + * @param string parameter value if $name is not an array
  254 + * @access public
  255 + */
  256 + function setRequestParameter($name, $value = null)
  257 + {
  258 + if (is_array($name)) {
  259 + $this->_defaultRequestParams = array_merge($this->_defaultRequestParams, $name);
  260 + } else {
  261 + $this->_defaultRequestParams[$name] = $value;
  262 + }
  263 + }
  264 +
  265 +
  266 + /**
  267 + * Performs a request, processes redirects
  268 + *
  269 + * @param object HTTP_Request object
  270 + * @access private
  271 + * @return integer HTTP response code
  272 + * @throws PEAR_Error
  273 + */
  274 + function _performRequest(&$request)
  275 + {
  276 + // If this is not a redirect, notify the listeners of new request
  277 + if (0 == $this->_redirectCount) {
  278 + $this->_notify('request', $request->_url->getUrl());
  279 + }
  280 + if (PEAR::isError($err = $request->sendRequest())) {
  281 + return $err;
  282 + }
  283 + $this->_pushResponse($request);
  284 +
  285 + $code = $request->getResponseCode();
  286 + if ($this->_maxRedirects > 0 && in_array($code, array(300, 301, 302, 303, 307))) {
  287 + if (++$this->_redirectCount > $this->_maxRedirects) {
  288 + return PEAR::raiseError('Too many redirects');
  289 + }
  290 + $location = $request->getResponseHeader('Location');
  291 + if ('' == $location) {
  292 + return PEAR::raiseError("No 'Location' field on redirect");
  293 + }
  294 + $url = $this->_redirectUrl($request->_url, $location);
  295 + // Notify of redirection
  296 + $this->_notify('httpRedirect', $url);
  297 + // we access the private properties directly, as there are no accessors for them
  298 + switch ($request->_method) {
  299 + case HTTP_REQUEST_METHOD_POST:
  300 + if (302 == $code || 303 == $code) {
  301 + return $this->get($url);
  302 + } else {
  303 + $postFiles = array();
  304 + foreach ($request->_postFiles as $name => $data) {
  305 + $postFiles[] = array($name, $data['name'], $data['type']);
  306 + }
  307 + return $this->post($url, $request->_postData, true, $postFiles);
  308 + }
  309 + case HTTP_REQUEST_METHOD_HEAD:
  310 + return (303 == $code? $this->get($url): $this->head($url));
  311 + case HTTP_REQUEST_METHOD_GET:
  312 + default:
  313 + return $this->get($url);
  314 + } // switch
  315 +
  316 + } else {
  317 + $this->_redirectCount = 0;
  318 + if (400 >= $code) {
  319 + $this->_notify('httpSuccess');
  320 + $this->setDefaultHeader('Referer', $request->_url->getUrl());
  321 + // some result processing should go here
  322 + } else {
  323 + $this->_notify('httpError');
  324 + }
  325 + }
  326 + return $code;
  327 + }
  328 +
  329 +
  330 + /**
  331 + * Returns the most recent HTTP response
  332 + *
  333 + * @access public
  334 + * @return array
  335 + */
  336 + function &currentResponse()
  337 + {
  338 + return $this->_responses[count($this->_responses) - 1];
  339 + }
  340 +
  341 +
  342 + /**
  343 + * Saves the server's response to responses list
  344 + *
  345 + * @param object HTTP_Request object, with request already sent
  346 + * @access private
  347 + */
  348 + function _pushResponse(&$request)
  349 + {
  350 + $this->_cookieManager->updateCookies($request);
  351 + $idx = $this->_isHistoryEnabled? count($this->_responses): 0;
  352 + $this->_responses[$idx] = array(
  353 + 'code' => $request->getResponseCode(),
  354 + 'headers' => $request->getResponseHeader(),
  355 + 'body' => $request->getResponseBody()
  356 + );
  357 + }
  358 +
  359 +
  360 + /**
  361 + * Clears object's internal properties
  362 + *
  363 + * @access public
  364 + */
  365 + function reset()
  366 + {
  367 + $this->_cookieManager->reset();
  368 + $this->_responses = array();
  369 + $this->_defaultHeaders = array();
  370 + $this->_defaultRequestParams = array();
  371 + }
  372 +
  373 +
  374 + /**
  375 + * Adds a Listener to the list of listeners that are notified of
  376 + * the object's events
  377 + *
  378 + * @param object HTTP_Request_Listener instance to attach
  379 + * @param boolean Whether the listener should be attached to the
  380 + * created HTTP_Request objects
  381 + * @return boolean whether the listener was successfully attached
  382 + * @access public
  383 + */
  384 + function attach(&$listener, $propagate = false)
  385 + {
  386 + if (!is_a($listener, 'HTTP_Request_Listener')) {
  387 + return false;
  388 + }
  389 + $this->_listeners[$listener->getId()] =& $listener;
  390 + $this->_propagate[$listener->getId()] = $propagate;
  391 + return true;
  392 + }
  393 +
  394 +
  395 + /**
  396 + * Removes a Listener from the list of listeners
  397 + *
  398 + * @param object HTTP_Request_Listener instance to detach
  399 + * @return boolean whether the listener was successfully detached
  400 + * @access public
  401 + */
  402 + function detach(&$listener)
  403 + {
  404 + if (!is_a($listener, 'HTTP_Request_Listener') ||
  405 + !isset($this->_listeners[$listener->getId()])) {
  406 + return false;
  407 + }
  408 + unset($this->_listeners[$listener->getId()], $this->_propagate[$listener->getId()]);
  409 + return true;
  410 + }
  411 +
  412 +
  413 + /**
  414 + * Notifies all registered listeners of an event.
  415 + *
  416 + * Currently available events are:
  417 + * 'request': sent on HTTP request that is not a redirect
  418 + * 'httpSuccess': sent when we receive a successfull 2xx response
  419 + * 'httpRedirect': sent when we receive a redirection response
  420 + * 'httpError': sent on 4xx, 5xx response
  421 + *
  422 + * @param string Event name
  423 + * @param mixed Additional data
  424 + * @access private
  425 + */
  426 + function _notify($event, $data = null)
  427 + {
  428 + foreach (array_keys($this->_listeners) as $id) {
  429 + $this->_listeners[$id]->update($this, $event, $data);
  430 + }
  431 + }
  432 +
  433 +
  434 + /**
  435 + * Calculates the absolute URL of a redirect
  436 + *
  437 + * @param object Net_Url object containing the request URL
  438 + * @param string Value of the 'Location' response header
  439 + * @return string Absolute URL we are being redirected to
  440 + * @access private
  441 + */
  442 + function _redirectUrl($url, $location)
  443 + {
  444 + if (preg_match('!^https?://!i', $location)) {
  445 + return $location;
  446 + } else {
  447 + if ('/' == $location{0}) {
  448 + $url->path = Net_URL::resolvePath($location);
  449 + } elseif('/' == substr($url->path, -1)) {
  450 + $url->path = Net_URL::resolvePath($url->path . $location);
  451 + } else {
  452 + $dirname = (DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path));
  453 + $url->path = Net_URL::resolvePath($dirname . '/' . $location);
  454 + }
  455 + $url->querystring = array();
  456 + $url->anchor = '';
  457 + return $url->getUrl();
  458 + }
  459 + }
  460 +}
  461 +?>
... ...
thirdparty/pear/WebDAV/Client/CookieManager.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4: */
  3 +// +----------------------------------------------------------------------+
  4 +// | PHP version 4 |
  5 +// +----------------------------------------------------------------------+
  6 +// | Copyright (c) 1997-2003 The PHP Group |
  7 +// +----------------------------------------------------------------------+
  8 +// | This source file is subject to version 3.0 of the PHP license, |
  9 +// | that is bundled with this package in the file LICENSE, and is |
  10 +// | available through the world-wide-web at |
  11 +// | http://www.php.net/license/3_0.txt. |
  12 +// | If you did not receive a copy of the PHP license and are unable to |
  13 +// | obtain it through the world-wide-web, please send a note to |
  14 +// | license@php.net so we can mail you a copy immediately. |
  15 +// +----------------------------------------------------------------------+
  16 +// | Author: Alexey Borzov <avb@php.net> |
  17 +// +----------------------------------------------------------------------+
  18 +//
  19 +// $Id: CookieManager.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  20 +
  21 +/**
  22 + * This class is used to store cookies and pass them between HTTP requests.
  23 + *
  24 + * @package HTTP_Client
  25 + * @author Alexey Borzov <avb@php.net>
  26 + * @version $Revision: 6819 $
  27 + */
  28 +class HTTP_Client_CookieManager
  29 +{
  30 + /**
  31 + * An array containing cookie values
  32 + * @var array
  33 + */
  34 + var $_cookies = array();
  35 +
  36 +
  37 + /**
  38 + * Constructor
  39 + *
  40 + * @access public
  41 + */
  42 + function HTTP_Client_CookieManager()
  43 + {
  44 + // abstract
  45 + }
  46 +
  47 +
  48 + /**
  49 + * Adds cookies to the request
  50 + *
  51 + * @access public
  52 + * @param object An HTTP_Request object
  53 + */
  54 + function passCookies(&$request)
  55 + {
  56 + if (!empty($this->_cookies)) {
  57 + $url =& $request->_url;
  58 + // We do not check cookie's "expires" field, as we do not store deleted
  59 + // cookies in the array and our client does not work long enough for other
  60 + // cookies to expire. If some kind of persistence is added to this object,
  61 + // then expiration should be checked upon loading and session cookies should
  62 + // be cleared on saving.
  63 + $cookies = array();
  64 + foreach ($this->_cookies as $cookie) {
  65 + if ($this->_domainMatch($url->host, $cookie['domain']) && (0 === strpos($url->path, $cookie['path']))
  66 + && (empty($cookie['secure']) || $url->protocol == 'https')) {
  67 + $cookies[$cookie['name']][strlen($cookie['path'])] = $cookie['value'];
  68 + }
  69 + }
  70 + // cookies with longer paths go first
  71 + foreach ($cookies as $name => $values) {
  72 + krsort($values);
  73 + foreach ($values as $value) {
  74 + $request->addCookie($name, $value);
  75 + }
  76 + }
  77 + }
  78 + return true;
  79 + }
  80 +
  81 +
  82 + /**
  83 + * Explicitly adds cookie to the list
  84 + *
  85 + * @param array An array representing cookie, this function expects all of the array's
  86 + * fields to be set
  87 + * @access public
  88 + */
  89 + function addCookie($cookie)
  90 + {
  91 + $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
  92 + $this->_cookies[$hash] = $cookie;
  93 + }
  94 +
  95 +
  96 + /**
  97 + * Updates cookie list from HTTP server response
  98 + *
  99 + * @access public
  100 + * @param object An HTTP_Request object with sendRequest() already done
  101 + */
  102 + function updateCookies(&$request)
  103 + {
  104 + if (false !== ($cookies = $request->getResponseCookies())) {
  105 + $url =& $request->_url;
  106 + foreach ($cookies as $cookie) {
  107 + // use the current domain by default
  108 + if (!isset($cookie['domain'])) {
  109 + $cookie['domain'] = $url->host;
  110 + }
  111 + // use the path to the current page by default
  112 + if (!isset($cookie['path'])) {
  113 + $cookie['path'] = DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path);
  114 + }
  115 + // check if the domains match
  116 + if ($this->_domainMatch($url->host, $cookie['domain'])) {
  117 + $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
  118 + // if value is empty or the time is in the past the cookie is deleted, else added
  119 + if (strlen($cookie['value'])
  120 + && (!isset($cookie['expires']) || (strtotime($cookie['expires']) > time()))) {
  121 + $this->_cookies[$hash] = $cookie;
  122 + } elseif (isset($this->_cookies[$hash])) {
  123 + unset($this->_cookies[$hash]);
  124 + }
  125 + }
  126 + }
  127 + }
  128 + }
  129 +
  130 +
  131 + /**
  132 + * Generates a key for the $_cookies array.
  133 + *
  134 + * The cookies is uniquely identified by its name, domain and path.
  135 + * Thus we cannot make f.e. an associative array with name as a key, we should
  136 + * generate a key from these 3 values.
  137 + *
  138 + * @access private
  139 + * @param string Cookie name
  140 + * @param string Cookie domain
  141 + * @param string Cookie path
  142 + * @return string a key
  143 + */
  144 + function _makeHash($name, $domain, $path)
  145 + {
  146 + return md5($name . "\r\n" . $domain . "\r\n" . $path);
  147 + }
  148 +
  149 +
  150 + /**
  151 + * Checks whether a cookie domain matches a request host.
  152 + *
  153 + * Cookie domain can begin with a dot, it also must contain at least
  154 + * two dots.
  155 + *
  156 + * @access private
  157 + * @param string request host
  158 + * @param string cookie domain
  159 + * @return bool match success
  160 + */
  161 + function _domainMatch($requestHost, $cookieDomain)
  162 + {
  163 + if ('.' != $cookieDomain{0}) {
  164 + return $requestHost == $cookieDomain;
  165 + } elseif (substr_count($cookieDomain, '.') < 2) {
  166 + return false;
  167 + } else {
  168 + return substr('.'. $requestHost, - strlen($cookieDomain)) == $cookieDomain;
  169 + }
  170 + }
  171 +
  172 +
  173 + /**
  174 + * Clears the $_cookies array
  175 + *
  176 + * @access public
  177 + */
  178 + function reset()
  179 + {
  180 + $this->_cookies = array();
  181 + }
  182 +}
  183 +?>
... ...
thirdparty/pear/WebDAV/Download.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Download
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Download
  11 + * @author Michael Wallner <mike@php.net>
  12 + * @copyright 2003-2005 Michael Wallner
  13 + * @license BSD, revised
  14 + * @version CVS: $Id: Download.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  15 + * @link http://pear.php.net/package/HTTP_Download
  16 + */
  17 +
  18 +// {{{ includes
  19 +/**
  20 + * Requires PEAR
  21 + */
  22 +require_once 'PEAR.php';
  23 +
  24 +/**
  25 + * Requires HTTP_Header
  26 + */
  27 +require_once 'HTTP/Header.php';
  28 +// }}}
  29 +
  30 +// {{{ constants
  31 +/**#@+ Use with HTTP_Download::setContentDisposition() **/
  32 +/**
  33 + * Send data as attachment
  34 + */
  35 +define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment');
  36 +/**
  37 + * Send data inline
  38 + */
  39 +define('HTTP_DOWNLOAD_INLINE', 'inline');
  40 +/**#@-**/
  41 +
  42 +/**#@+ Use with HTTP_Download::sendArchive() **/
  43 +/**
  44 + * Send as uncompressed tar archive
  45 + */
  46 +define('HTTP_DOWNLOAD_TAR', 'TAR');
  47 +/**
  48 + * Send as gzipped tar archive
  49 + */
  50 +define('HTTP_DOWNLOAD_TGZ', 'TGZ');
  51 +/**
  52 + * Send as bzip2 compressed tar archive
  53 + */
  54 +define('HTTP_DOWNLOAD_BZ2', 'BZ2');
  55 +/**
  56 + * Send as zip archive
  57 + */
  58 +define('HTTP_DOWNLOAD_ZIP', 'ZIP');
  59 +/**#@-**/
  60 +
  61 +/**#@+
  62 + * Error constants
  63 + */
  64 +define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1);
  65 +define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2);
  66 +define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3);
  67 +define('HTTP_DOWNLOAD_E_INVALID_FILE', -4);
  68 +define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5);
  69 +define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6);
  70 +define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7);
  71 +define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8);
  72 +define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9);
  73 +/**#@-**/
  74 +// }}}
  75 +
  76 +/**
  77 + * Send HTTP Downloads/Responses.
  78 + *
  79 + * With this package you can handle (hidden) downloads.
  80 + * It supports partial downloads, resuming and sending
  81 + * raw data ie. from database BLOBs.
  82 + *
  83 + * <i>ATTENTION:</i>
  84 + * You shouldn't use this package together with ob_gzhandler or
  85 + * zlib.output_compression enabled in your php.ini, especially
  86 + * if you want to send already gzipped data!
  87 + *
  88 + * @access public
  89 + * @version $Revision: 6819 $
  90 + */
  91 +class HTTP_Download
  92 +{
  93 + // {{{ protected member variables
  94 + /**
  95 + * Path to file for download
  96 + *
  97 + * @see HTTP_Download::setFile()
  98 + * @access protected
  99 + * @var string
  100 + */
  101 + var $file = '';
  102 +
  103 + /**
  104 + * Data for download
  105 + *
  106 + * @see HTTP_Download::setData()
  107 + * @access protected
  108 + * @var string
  109 + */
  110 + var $data = null;
  111 +
  112 + /**
  113 + * Resource handle for download
  114 + *
  115 + * @see HTTP_Download::setResource()
  116 + * @access protected
  117 + * @var int
  118 + */
  119 + var $handle = null;
  120 +
  121 + /**
  122 + * Whether to gzip the download
  123 + *
  124 + * @access protected
  125 + * @var bool
  126 + */
  127 + var $gzip = false;
  128 +
  129 + /**
  130 + * Whether to allow caching of the download on the clients side
  131 + *
  132 + * @access protected
  133 + * @var bool
  134 + */
  135 + var $cache = true;
  136 +
  137 + /**
  138 + * Size of download
  139 + *
  140 + * @access protected
  141 + * @var int
  142 + */
  143 + var $size = 0;
  144 +
  145 + /**
  146 + * Last modified
  147 + *
  148 + * @access protected
  149 + * @var int
  150 + */
  151 + var $lastModified = 0;
  152 +
  153 + /**
  154 + * HTTP headers
  155 + *
  156 + * @access protected
  157 + * @var array
  158 + */
  159 + var $headers = array(
  160 + 'Content-Type' => 'application/x-octetstream',
  161 + 'Pragma' => 'cache',
  162 + 'Cache-Control' => 'public, must-revalidate, max-age=0',
  163 + 'Accept-Ranges' => 'bytes',
  164 + 'X-Sent-By' => 'PEAR::HTTP::Download'
  165 + );
  166 +
  167 + /**
  168 + * HTTP_Header
  169 + *
  170 + * @access protected
  171 + * @var object
  172 + */
  173 + var $HTTP = null;
  174 +
  175 + /**
  176 + * ETag
  177 + *
  178 + * @access protected
  179 + * @var string
  180 + */
  181 + var $etag = '';
  182 +
  183 + /**
  184 + * Buffer Size
  185 + *
  186 + * @access protected
  187 + * @var int
  188 + */
  189 + var $bufferSize = 2097152;
  190 +
  191 + /**
  192 + * Throttle Delay
  193 + *
  194 + * @access protected
  195 + * @var float
  196 + */
  197 + var $throttleDelay = 0;
  198 +
  199 + /**
  200 + * Sent Bytes
  201 + *
  202 + * @access public
  203 + * @var int
  204 + */
  205 + var $sentBytes = 0;
  206 + // }}}
  207 +
  208 + // {{{ constructor
  209 + /**
  210 + * Constructor
  211 + *
  212 + * Set supplied parameters.
  213 + *
  214 + * @access public
  215 + * @param array $params associative array of parameters
  216 + *
  217 + * <b>one of:</b>
  218 + * o 'file' => path to file for download
  219 + * o 'data' => raw data for download
  220 + * o 'resource' => resource handle for download
  221 + * <br/>
  222 + * <b>and any of:</b>
  223 + * o 'cache' => whether to allow cs caching
  224 + * o 'gzip' => whether to gzip the download
  225 + * o 'lastmodified' => unix timestamp
  226 + * o 'contenttype' => content type of download
  227 + * o 'contentdisposition' => content disposition
  228 + * o 'buffersize' => amount of bytes to buffer
  229 + * o 'throttledelay' => amount of secs to sleep
  230 + * o 'cachecontrol' => cache privacy and validity
  231 + *
  232 + * <br />
  233 + * 'Content-Disposition' is not HTTP compliant, but most browsers
  234 + * follow this header, so it was borrowed from MIME standard.
  235 + *
  236 + * It looks like this: <br />
  237 + * "Content-Disposition: attachment; filename=example.tgz".
  238 + *
  239 + * @see HTTP_Download::setContentDisposition()
  240 + */
  241 + function HTTP_Download($params = array())
  242 + {
  243 + $this->HTTP = &new HTTP_Header;
  244 + $this->setParams($params);
  245 + }
  246 + // }}}
  247 +
  248 + // {{{ public methods
  249 + /**
  250 + * Set parameters
  251 + *
  252 + * Set supplied parameters through its accessor methods.
  253 + *
  254 + * @access public
  255 + * @return mixed Returns true on success or PEAR_Error on failure.
  256 + * @param array $params associative array of parameters
  257 + *
  258 + * @see HTTP_Download::HTTP_Download()
  259 + */
  260 + function setParams($params)
  261 + {
  262 + foreach((array) $params as $param => $value){
  263 + $method = 'set'. $param;
  264 +
  265 + if (!method_exists($this, $method)) {
  266 + return PEAR::raiseError(
  267 + "Method '$method' doesn't exist.",
  268 + HTTP_DOWNLOAD_E_INVALID_PARAM
  269 + );
  270 + }
  271 +
  272 + $e = call_user_func_array(array(&$this, $method), (array) $value);
  273 +
  274 + if (PEAR::isError($e)) {
  275 + return $e;
  276 + }
  277 + }
  278 + return true;
  279 + }
  280 +
  281 + /**
  282 + * Set path to file for download
  283 + *
  284 + * The Last-Modified header will be set to files filemtime(), actually.
  285 + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist.
  286 + * Sends HTTP 404 status if $send_404 is set to true.
  287 + *
  288 + * @access public
  289 + * @return mixed Returns true on success or PEAR_Error on failure.
  290 + * @param string $file path to file for download
  291 + * @param bool $send_404 whether to send HTTP/404 if
  292 + * the file wasn't found
  293 + */
  294 + function setFile($file, $send_404 = true)
  295 + {
  296 + $file = realpath($file);
  297 + if (!is_file($file)) {
  298 + if ($send_404) {
  299 + $this->HTTP->sendStatusCode(404);
  300 + }
  301 + return PEAR::raiseError(
  302 + "File '$file' not found.",
  303 + HTTP_DOWNLOAD_E_INVALID_FILE
  304 + );
  305 + }
  306 + $this->setLastModified(filemtime($file));
  307 + $this->file = $file;
  308 + $this->size = filesize($file);
  309 + return true;
  310 + }
  311 +
  312 + /**
  313 + * Set data for download
  314 + *
  315 + * Set $data to null if you want to unset this.
  316 + *
  317 + * @access public
  318 + * @return void
  319 + * @param $data raw data to send
  320 + */
  321 + function setData($data = null)
  322 + {
  323 + $this->data = $data;
  324 + $this->size = strlen($data);
  325 + }
  326 +
  327 + /**
  328 + * Set resource for download
  329 + *
  330 + * The resource handle supplied will be closed after sending the download.
  331 + * Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle
  332 + * is no valid resource. Set $handle to null if you want to unset this.
  333 + *
  334 + * @access public
  335 + * @return mixed Returns true on success or PEAR_Error on failure.
  336 + * @param int $handle resource handle
  337 + */
  338 + function setResource($handle = null)
  339 + {
  340 + if (!isset($handle)) {
  341 + $this->handle = null;
  342 + $this->size = 0;
  343 + return true;
  344 + }
  345 +
  346 + if (is_resource($handle)) {
  347 + $this->handle = $handle;
  348 + $filestats = fstat($handle);
  349 + $this->size = $filestats['size'];
  350 + return true;
  351 + }
  352 +
  353 + return PEAR::raiseError(
  354 + "Handle '$handle' is no valid resource.",
  355 + HTTP_DOWNLOAD_E_INVALID_RESOURCE
  356 + );
  357 + }
  358 +
  359 + /**
  360 + * Whether to gzip the download
  361 + *
  362 + * Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB)
  363 + * if ext/zlib is not available/loadable.
  364 + *
  365 + * @access public
  366 + * @return mixed Returns true on success or PEAR_Error on failure.
  367 + * @param bool $gzip whether to gzip the download
  368 + */
  369 + function setGzip($gzip = false)
  370 + {
  371 + if ($gzip && !PEAR::loadExtension('zlib')){
  372 + return PEAR::raiseError(
  373 + 'GZIP compression (ext/zlib) not available.',
  374 + HTTP_DOWNLOAD_E_NO_EXT_ZLIB
  375 + );
  376 + }
  377 + $this->gzip = (bool) $gzip;
  378 + return true;
  379 + }
  380 +
  381 + /**
  382 + * Whether to allow caching
  383 + *
  384 + * If set to true (default) we'll send some headers that are commonly
  385 + * used for caching purposes like ETag, Cache-Control and Last-Modified.
  386 + *
  387 + * If caching is disabled, we'll send the download no matter if it
  388 + * would actually be cached at the client side.
  389 + *
  390 + * @access public
  391 + * @return void
  392 + * @param bool $cache whether to allow caching
  393 + */
  394 + function setCache($cache = true)
  395 + {
  396 + $this->cache = (bool) $cache;
  397 + }
  398 +
  399 + /**
  400 + * Whether to allow proxies to cache
  401 + *
  402 + * If set to 'private' proxies shouldn't cache the response.
  403 + * This setting defaults to 'public' and affects only cached responses.
  404 + *
  405 + * @access public
  406 + * @return bool
  407 + * @param string $cache private or public
  408 + * @param int $maxage maximum age of the client cache entry
  409 + */
  410 + function setCacheControl($cache = 'public', $maxage = 0)
  411 + {
  412 + switch ($cache = strToLower($cache))
  413 + {
  414 + case 'private':
  415 + case 'public':
  416 + $this->headers['Cache-Control'] =
  417 + $cache .', must-revalidate, max-age='. abs($maxage);
  418 + return true;
  419 + break;
  420 + }
  421 + return false;
  422 + }
  423 +
  424 + /**
  425 + * Set ETag
  426 + *
  427 + * Sets a user-defined ETag for cache-validation. The ETag is usually
  428 + * generated by HTTP_Download through its payload information.
  429 + *
  430 + * @access public
  431 + * @return void
  432 + * @param string $etag Entity tag used for strong cache validation.
  433 + */
  434 + function setETag($etag = null)
  435 + {
  436 + $this->etag = (string) $etag;
  437 + }
  438 +
  439 + /**
  440 + * Set Size of Buffer
  441 + *
  442 + * The amount of bytes specified as buffer size is the maximum amount
  443 + * of data read at once from resources or files. The default size is 2M
  444 + * (2097152 bytes). Be aware that if you enable gzip compression and
  445 + * you set a very low buffer size that the actual file size may grow
  446 + * due to added gzip headers for each sent chunk of the specified size.
  447 + *
  448 + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not
  449 + * greater than 0 bytes.
  450 + *
  451 + * @access public
  452 + * @return mixed Returns true on success or PEAR_Error on failure.
  453 + * @param int $bytes Amount of bytes to use as buffer.
  454 + */
  455 + function setBufferSize($bytes = 2097152)
  456 + {
  457 + if (0 >= $bytes) {
  458 + return PEAR::raiseError(
  459 + 'Buffer size must be greater than 0 bytes ('. $bytes .' given)',
  460 + HTTP_DOWNLOAD_E_INVALID_PARAM);
  461 + }
  462 + $this->bufferSize = abs($bytes);
  463 + return true;
  464 + }
  465 +
  466 + /**
  467 + * Set Throttle Delay
  468 + *
  469 + * Set the amount of seconds to sleep after each chunck that has been
  470 + * sent. One can implement some sort of throttle through adjusting the
  471 + * buffer size and the throttle delay. With the following settings
  472 + * HTTP_Download will sleep a second after each 25 K of data sent.
  473 + *
  474 + * <code>
  475 + * Array(
  476 + * 'throttledelay' => 1,
  477 + * 'buffersize' => 1024 * 25,
  478 + * )
  479 + * </code>
  480 + *
  481 + * Just be aware that if gzipp'ing is enabled, decreasing the chunk size
  482 + * too much leads to proportionally increased network traffic due to added
  483 + * gzip header and bottom bytes around each chunk.
  484 + *
  485 + * @access public
  486 + * @return void
  487 + * @param float $seconds Amount of seconds to sleep after each
  488 + * chunk that has been sent.
  489 + */
  490 + function setThrottleDelay($seconds = 0)
  491 + {
  492 + $this->throttleDelay = abs($seconds) * 1000;
  493 + }
  494 +
  495 + /**
  496 + * Set "Last-Modified"
  497 + *
  498 + * This is usually determined by filemtime() in HTTP_Download::setFile()
  499 + * If you set raw data for download with HTTP_Download::setData() and you
  500 + * want do send an appropiate "Last-Modified" header, you should call this
  501 + * method.
  502 + *
  503 + * @access public
  504 + * @return void
  505 + * @param int unix timestamp
  506 + */
  507 + function setLastModified($last_modified)
  508 + {
  509 + $this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified;
  510 + }
  511 +
  512 + /**
  513 + * Set Content-Disposition header
  514 + *
  515 + * @see HTTP_Download::HTTP_Download
  516 + *
  517 + * @access public
  518 + * @return void
  519 + * @param string $disposition whether to send the download
  520 + * inline or as attachment
  521 + * @param string $file_name the filename to display in
  522 + * the browser's download window
  523 + *
  524 + * <b>Example:</b>
  525 + * <code>
  526 + * $HTTP_Download->setContentDisposition(
  527 + * HTTP_DOWNLOAD_ATTACHMENT,
  528 + * 'download.tgz'
  529 + * );
  530 + * </code>
  531 + */
  532 + function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT,
  533 + $file_name = null)
  534 + {
  535 + $cd = $disposition;
  536 + if (isset($file_name)) {
  537 + $cd .= '; filename="' . $file_name . '"';
  538 + } elseif ($this->file) {
  539 + $cd .= '; filename="' . basename($this->file) . '"';
  540 + }
  541 + $this->headers['Content-Disposition'] = $cd;
  542 + }
  543 +
  544 + /**
  545 + * Set content type of the download
  546 + *
  547 + * Default content type of the download will be 'application/x-octetstream'.
  548 + * Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if
  549 + * $content_type doesn't seem to be valid.
  550 + *
  551 + * @access public
  552 + * @return mixed Returns true on success or PEAR_Error on failure.
  553 + * @param string $content_type content type of file for download
  554 + */
  555 + function setContentType($content_type = 'application/x-octetstream')
  556 + {
  557 + if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) {
  558 + return PEAR::raiseError(
  559 + "Invalid content type '$content_type' supplied.",
  560 + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
  561 + );
  562 + }
  563 + $this->headers['Content-Type'] = $content_type;
  564 + return true;
  565 + }
  566 +
  567 + /**
  568 + * Guess content type of file
  569 + *
  570 + * First we try to use PEAR::MIME_Type, if installed, to detect the content
  571 + * type, else we check if ext/mime_magic is loaded and properly configured.
  572 + *
  573 + * Returns PEAR_Error if:
  574 + * o if PEAR::MIME_Type failed to detect a proper content type
  575 + * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
  576 + * o ext/magic.mime is not installed, or not properly configured
  577 + * (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC)
  578 + * o mime_content_type() couldn't guess content type or returned
  579 + * a content type considered to be bogus by setContentType()
  580 + * (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
  581 + *
  582 + * @access public
  583 + * @return mixed Returns true on success or PEAR_Error on failure.
  584 + */
  585 + function guessContentType()
  586 + {
  587 + if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') {
  588 + if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) {
  589 + return PEAR::raiseError($mime_type->getMessage(),
  590 + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE);
  591 + }
  592 + return $this->setContentType($mime_type);
  593 + }
  594 + if (!function_exists('mime_content_type')) {
  595 + return PEAR::raiseError(
  596 + 'This feature requires ext/mime_magic!',
  597 + HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
  598 + );
  599 + }
  600 + if (!is_file(ini_get('mime_magic.magicfile'))) {
  601 + return PEAR::raiseError(
  602 + 'ext/mime_magic is loaded but not properly configured!',
  603 + HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
  604 + );
  605 + }
  606 + if (!$content_type = @mime_content_type($this->file)) {
  607 + return PEAR::raiseError(
  608 + 'Couldn\'t guess content type with mime_content_type().',
  609 + HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
  610 + );
  611 + }
  612 + return $this->setContentType($content_type);
  613 + }
  614 +
  615 + /**
  616 + * Send
  617 + *
  618 + * Returns PEAR_Error if:
  619 + * o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT)
  620 + * o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST)
  621 + *
  622 + * @access public
  623 + * @return mixed Returns true on success or PEAR_Error on failure.
  624 + * @param bool $autoSetContentDisposition Whether to set the
  625 + * Content-Disposition header if it isn't already.
  626 + */
  627 + function send($autoSetContentDisposition = true)
  628 + {
  629 + if (headers_sent()) {
  630 + return PEAR::raiseError(
  631 + 'Headers already sent.',
  632 + HTTP_DOWNLOAD_E_HEADERS_SENT
  633 + );
  634 + }
  635 +
  636 + if (!ini_get('safe_mode')) {
  637 + @set_time_limit(0);
  638 + }
  639 +
  640 + if ($autoSetContentDisposition &&
  641 + !isset($this->headers['Content-Disposition'])) {
  642 + $this->setContentDisposition();
  643 + }
  644 +
  645 + if ($this->cache) {
  646 + $this->headers['ETag'] = $this->generateETag();
  647 + if ($this->isCached()) {
  648 + $this->HTTP->sendStatusCode(304);
  649 + $this->sendHeaders();
  650 + return true;
  651 + }
  652 + } else {
  653 + unset($this->headers['Last-Modified']);
  654 + }
  655 +
  656 + while (@ob_end_clean());
  657 +
  658 + if ($this->gzip) {
  659 + @ob_start('ob_gzhandler');
  660 + } else {
  661 + ob_start();
  662 + }
  663 +
  664 + $this->sentBytes = 0;
  665 +
  666 + if ($this->isRangeRequest()) {
  667 + $this->HTTP->sendStatusCode(206);
  668 + $chunks = $this->getChunks();
  669 + } else {
  670 + $this->HTTP->sendStatusCode(200);
  671 + $chunks = array(array(0, $this->size));
  672 + if (!$this->gzip && count(ob_list_handlers()) < 2) {
  673 + $this->headers['Content-Length'] = $this->size;
  674 + }
  675 + }
  676 +
  677 + if (PEAR::isError($e = $this->sendChunks($chunks))) {
  678 + ob_end_clean();
  679 + $this->HTTP->sendStatusCode(416);
  680 + return $e;
  681 + }
  682 +
  683 + ob_end_flush();
  684 + flush();
  685 + return true;
  686 + }
  687 +
  688 + /**
  689 + * Static send
  690 + *
  691 + * @see HTTP_Download::HTTP_Download()
  692 + * @see HTTP_Download::send()
  693 + *
  694 + * @static
  695 + * @access public
  696 + * @return mixed Returns true on success or PEAR_Error on failure.
  697 + * @param array $params associative array of parameters
  698 + * @param bool $guess whether HTTP_Download::guessContentType()
  699 + * should be called
  700 + */
  701 + function staticSend($params, $guess = false)
  702 + {
  703 + $d = &new HTTP_Download();
  704 + $e = $d->setParams($params);
  705 + if (PEAR::isError($e)) {
  706 + return $e;
  707 + }
  708 + if ($guess) {
  709 + $e = $d->guessContentType();
  710 + if (PEAR::isError($e)) {
  711 + return $e;
  712 + }
  713 + }
  714 + return $d->send();
  715 + }
  716 +
  717 + /**
  718 + * Send a bunch of files or directories as an archive
  719 + *
  720 + * Example:
  721 + * <code>
  722 + * require_once 'HTTP/Download.php';
  723 + * HTTP_Download::sendArchive(
  724 + * 'myArchive.tgz',
  725 + * '/var/ftp/pub/mike',
  726 + * HTTP_DOWNLOAD_TGZ,
  727 + * '',
  728 + * '/var/ftp/pub'
  729 + * );
  730 + * </code>
  731 + *
  732 + * @see Archive_Tar::createModify()
  733 + * @deprecated use HTTP_Download_Archive::send()
  734 + * @static
  735 + * @access public
  736 + * @return mixed Returns true on success or PEAR_Error on failure.
  737 + * @param string $name name the sent archive should have
  738 + * @param mixed $files files/directories
  739 + * @param string $type archive type
  740 + * @param string $add_path path that should be prepended to the files
  741 + * @param string $strip_path path that should be stripped from the files
  742 + */
  743 + function sendArchive( $name,
  744 + $files,
  745 + $type = HTTP_DOWNLOAD_TGZ,
  746 + $add_path = '',
  747 + $strip_path = '')
  748 + {
  749 + require_once 'HTTP/Download/Archive.php';
  750 + return HTTP_Download_Archive::send($name, $files, $type,
  751 + $add_path, $strip_path);
  752 + }
  753 + // }}}
  754 +
  755 + // {{{ protected methods
  756 + /**
  757 + * Generate ETag
  758 + *
  759 + * @access protected
  760 + * @return string
  761 + */
  762 + function generateETag()
  763 + {
  764 + if (!$this->etag) {
  765 + if ($this->data) {
  766 + $md5 = md5($this->data);
  767 + } else {
  768 + $fst = is_resource($this->handle) ?
  769 + fstat($this->handle) : stat($this->file);
  770 + $md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']);
  771 + }
  772 + $this->etag = '"' . $md5 . '-' . crc32($md5) . '"';
  773 + }
  774 + return $this->etag;
  775 + }
  776 +
  777 + /**
  778 + * Send multiple chunks
  779 + *
  780 + * @access protected
  781 + * @return mixed Returns true on success or PEAR_Error on failure.
  782 + * @param array $chunks
  783 + */
  784 + function sendChunks($chunks)
  785 + {
  786 + if (count($chunks) == 1) {
  787 + return $this->sendChunk(array_shift($chunks));
  788 + }
  789 +
  790 + $bound = uniqid('HTTP_DOWNLOAD-', true);
  791 + $cType = $this->headers['Content-Type'];
  792 + $this->headers['Content-Type'] =
  793 + 'multipart/byteranges; boundary=' . $bound;
  794 + $this->sendHeaders();
  795 + foreach ($chunks as $chunk){
  796 + if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) {
  797 + return $e;
  798 + }
  799 + }
  800 + #echo "\r\n--$bound--\r\n";
  801 + return true;
  802 + }
  803 +
  804 + /**
  805 + * Send chunk of data
  806 + *
  807 + * @access protected
  808 + * @return mixed Returns true on success or PEAR_Error on failure.
  809 + * @param array $chunk start and end offset of the chunk to send
  810 + * @param string $cType actual content type
  811 + * @param string $bound boundary for multipart/byteranges
  812 + */
  813 + function sendChunk($chunk, $cType = null, $bound = null)
  814 + {
  815 + list($offset, $lastbyte) = $chunk;
  816 + $length = ($lastbyte - $offset) + 1;
  817 +
  818 + if ($length < 1) {
  819 + return PEAR::raiseError(
  820 + "Error processing range request: $offset-$lastbyte/$length",
  821 + HTTP_DOWNLOAD_E_INVALID_REQUEST
  822 + );
  823 + }
  824 +
  825 + $range = $offset . '-' . $lastbyte . '/' . $this->size;
  826 +
  827 + if (isset($cType, $bound)) {
  828 + echo "\r\n--$bound\r\n",
  829 + "Content-Type: $cType\r\n",
  830 + "Content-Range: bytes $range\r\n\r\n";
  831 + } else {
  832 + if ($this->isRangeRequest()) {
  833 + $this->headers['Content-Range'] = 'bytes '. $range;
  834 + }
  835 + $this->sendHeaders();
  836 + }
  837 +
  838 + if ($this->data) {
  839 + while (($length -= $this->bufferSize) > 0) {
  840 + $this->flush(substr($this->data, $offset, $this->bufferSize));
  841 + $this->throttleDelay and $this->sleep();
  842 + $offset += $this->bufferSize;
  843 + }
  844 + if ($length) {
  845 + $this->flush(substr($this->data, $offset, $this->bufferSize + $length));
  846 + }
  847 + } else {
  848 + if (!is_resource($this->handle)) {
  849 + $this->handle = fopen($this->file, 'rb');
  850 + }
  851 + fseek($this->handle, $offset);
  852 + while (($length -= $this->bufferSize) > 0) {
  853 + $this->flush(fread($this->handle, $this->bufferSize));
  854 + $this->throttleDelay and $this->sleep();
  855 + }
  856 + if ($length) {
  857 + $this->flush(fread($this->handle, $this->bufferSize + $length));
  858 + }
  859 + }
  860 + return true;
  861 + }
  862 +
  863 + /**
  864 + * Get chunks to send
  865 + *
  866 + * @access protected
  867 + * @return array
  868 + */
  869 + function getChunks()
  870 + {
  871 + $parts = array();
  872 + foreach (explode(',', $this->getRanges()) as $chunk){
  873 + list($o, $e) = explode('-', $chunk);
  874 + if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) {
  875 + $e = $this->size - 1;
  876 + }
  877 + if (empty($o) && $o !== 0 && $o !== '0') {
  878 + $o = $this->size - $e;
  879 + $e = $this->size - 1;
  880 + }
  881 + $parts[] = array($o, $e);
  882 + }
  883 + return $parts;
  884 + }
  885 +
  886 + /**
  887 + * Check if range is requested
  888 + *
  889 + * @access protected
  890 + * @return bool
  891 + */
  892 + function isRangeRequest()
  893 + {
  894 + if (!isset($_SERVER['HTTP_RANGE'])) {
  895 + return false;
  896 + }
  897 + return $this->isValidRange();
  898 + }
  899 +
  900 + /**
  901 + * Get range request
  902 + *
  903 + * @access protected
  904 + * @return array
  905 + */
  906 + function getRanges()
  907 + {
  908 + return preg_match('/^bytes=((\d*-\d*,? ?)+)$/',
  909 + @$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array();
  910 + }
  911 +
  912 + /**
  913 + * Check if entity is cached
  914 + *
  915 + * @access protected
  916 + * @return bool
  917 + */
  918 + function isCached()
  919 + {
  920 + return (
  921 + (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
  922 + $this->lastModified == strtotime(current($a = explode(
  923 + ';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) ||
  924 + (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
  925 + $this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag))
  926 + );
  927 + }
  928 +
  929 + /**
  930 + * Check if entity hasn't changed
  931 + *
  932 + * @access protected
  933 + * @return bool
  934 + */
  935 + function isValidRange()
  936 + {
  937 + if (isset($_SERVER['HTTP_IF_MATCH']) &&
  938 + !$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) {
  939 + return false;
  940 + }
  941 + if (isset($_SERVER['HTTP_IF_RANGE']) &&
  942 + $_SERVER['HTTP_IF_RANGE'] !== $this->etag &&
  943 + strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) {
  944 + return false;
  945 + }
  946 + if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
  947 + $lm = array_shift(explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']));
  948 + if (strtotime($lm) !== $this->lastModified) {
  949 + return false;
  950 + }
  951 + }
  952 + if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) {
  953 + $lm = array_shift(explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE']));
  954 + if (strtotime($lm) !== $this->lastModified) {
  955 + return false;
  956 + }
  957 + }
  958 + return true;
  959 + }
  960 +
  961 + /**
  962 + * Compare against an asterisk or check for equality
  963 + *
  964 + * @access protected
  965 + * @return bool
  966 + * @param string key for the $_SERVER array
  967 + * @param string string to compare
  968 + */
  969 + function compareAsterisk($svar, $compare)
  970 + {
  971 + foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) {
  972 + if ($request === '*' || $request === $compare) {
  973 + return true;
  974 + }
  975 + }
  976 + return false;
  977 + }
  978 +
  979 + /**
  980 + * Send HTTP headers
  981 + *
  982 + * @access protected
  983 + * @return void
  984 + */
  985 + function sendHeaders()
  986 + {
  987 + foreach ($this->headers as $header => $value) {
  988 + $this->HTTP->setHeader($header, $value);
  989 + }
  990 + $this->HTTP->sendHeaders();
  991 + /* NSAPI won't output anything if we did this */
  992 + if (strncasecmp(PHP_SAPI, 'nsapi', 5)) {
  993 + ob_flush();
  994 + flush();
  995 + }
  996 + }
  997 +
  998 + /**
  999 + * Flush
  1000 + *
  1001 + * @access protected
  1002 + * @return void
  1003 + * @param string $data
  1004 + */
  1005 + function flush($data = '')
  1006 + {
  1007 + if ($dlen = strlen($data)) {
  1008 + $this->sentBytes += $dlen;
  1009 + echo $data;
  1010 + }
  1011 + ob_flush();
  1012 + flush();
  1013 + }
  1014 +
  1015 + /**
  1016 + * Sleep
  1017 + *
  1018 + * @access protected
  1019 + * @return void
  1020 + */
  1021 + function sleep()
  1022 + {
  1023 + if (OS_WINDOWS) {
  1024 + com_message_pump($this->throttleDelay);
  1025 + } else {
  1026 + usleep($this->throttleDelay * 1000);
  1027 + }
  1028 + }
  1029 + // }}}
  1030 +}
  1031 +?>
... ...
thirdparty/pear/WebDAV/Download/Archive.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Download::Archive
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Download
  11 + * @author Michael Wallner <mike@php.net>
  12 + * @copyright 2003-2005 Michael Wallner
  13 + * @license BSD, revisewd
  14 + * @version CVS: $Id: Archive.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  15 + * @link http://pear.php.net/package/HTTP_Download
  16 + */
  17 +
  18 +/**
  19 + * Requires HTTP_Download
  20 + */
  21 +require_once 'HTTP/Download.php';
  22 +
  23 +/**
  24 + * Requires System
  25 + */
  26 +require_once 'System.php';
  27 +
  28 +/**
  29 + * HTTP_Download_Archive
  30 + *
  31 + * Helper class for sending Archives.
  32 + *
  33 + * @access public
  34 + * @version $Revision: 6819 $
  35 + */
  36 +class HTTP_Download_Archive
  37 +{
  38 + /**
  39 + * Send a bunch of files or directories as an archive
  40 + *
  41 + * Example:
  42 + * <code>
  43 + * require_once 'HTTP/Download/Archive.php';
  44 + * HTTP_Download_Archive::send(
  45 + * 'myArchive.tgz',
  46 + * '/var/ftp/pub/mike',
  47 + * HTTP_DOWNLOAD_BZ2,
  48 + * '',
  49 + * '/var/ftp/pub'
  50 + * );
  51 + * </code>
  52 + *
  53 + * @see Archive_Tar::createModify()
  54 + * @static
  55 + * @access public
  56 + * @return mixed Returns true on success or PEAR_Error on failure.
  57 + * @param string $name name the sent archive should have
  58 + * @param mixed $files files/directories
  59 + * @param string $type archive type
  60 + * @param string $add_path path that should be prepended to the files
  61 + * @param string $strip_path path that should be stripped from the files
  62 + */
  63 + function send($name, $files, $type = HTTP_DOWNLOAD_TGZ, $add_path = '', $strip_path = '')
  64 + {
  65 + $tmp = System::mktemp();
  66 +
  67 + switch ($type = strToUpper($type))
  68 + {
  69 + case HTTP_DOWNLOAD_TAR:
  70 + include_once 'Archive/Tar.php';
  71 + $arc = &new Archive_Tar($tmp);
  72 + $content_type = 'x-tar';
  73 + break;
  74 +
  75 + case HTTP_DOWNLOAD_TGZ:
  76 + include_once 'Archive/Tar.php';
  77 + $arc = &new Archive_Tar($tmp, 'gz');
  78 + $content_type = 'x-gzip';
  79 + break;
  80 +
  81 + case HTTP_DOWNLOAD_BZ2:
  82 + include_once 'Archive/Tar.php';
  83 + $arc = &new Archive_Tar($tmp, 'bz2');
  84 + $content_type = 'x-bzip2';
  85 + break;
  86 +
  87 + case HTTP_DOWNLOAD_ZIP:
  88 + include_once 'Archive/Zip.php';
  89 + $arc = &new Archive_Zip($tmp);
  90 + $content_type = 'x-zip';
  91 + break;
  92 +
  93 + default:
  94 + return PEAR::raiseError(
  95 + 'Archive type not supported: ' . $type,
  96 + HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE
  97 + );
  98 + }
  99 +
  100 + if ($type == HTTP_DOWNLOAD_ZIP) {
  101 + $options = array( 'add_path' => $add_path,
  102 + 'remove_path' => $strip_path);
  103 + if (!$arc->create($files, $options)) {
  104 + return PEAR::raiseError('Archive creation failed.');
  105 + }
  106 + } else {
  107 + if (!$e = $arc->createModify($files, $add_path, $strip_path)) {
  108 + return PEAR::raiseError('Archive creation failed.');
  109 + }
  110 + if (PEAR::isError($e)) {
  111 + return $e;
  112 + }
  113 + }
  114 + unset($arc);
  115 +
  116 + $dl = &new HTTP_Download(array('file' => $tmp));
  117 + $dl->setContentType('application/' . $content_type);
  118 + $dl->setContentDisposition(HTTP_DOWNLOAD_ATTACHMENT, $name);
  119 + return $dl->send();
  120 + }
  121 +}
  122 +?>
... ...
thirdparty/pear/WebDAV/Download/PgLOB.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Download::PgLOB
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Download
  11 + * @author Michael Wallner <mike@php.net>
  12 + * @copyright 2003-2005 Michael Wallner
  13 + * @license BSD, revised
  14 + * @version CVS: $Id: PgLOB.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  15 + * @link http://pear.php.net/package/HTTP_Download
  16 + */
  17 +
  18 +$GLOBALS['_HTTP_Download_PgLOB_Connection'] = null;
  19 +stream_register_wrapper('pglob', 'HTTP_Download_PgLOB');
  20 +
  21 +/**
  22 + * PgSQL large object stream interface for HTTP_Download
  23 + *
  24 + * Usage:
  25 + * <code>
  26 + * require_once 'HTTP/Download.php';
  27 + * require_once 'HTTP/Download/PgLOB.php';
  28 + * $db = &DB::connect('pgsql://user:pass@host/db');
  29 + * // or $db = pg_connect(...);
  30 + * $lo = HTTP_Download_PgLOB::open($db, 12345);
  31 + * $dl = &new HTTP_Download;
  32 + * $dl->setResource($lo);
  33 + * $dl->send()
  34 + * </code>
  35 + *
  36 + * @access public
  37 + * @version $Revision: 6819 $
  38 + */
  39 +class HTTP_Download_PgLOB
  40 +{
  41 + /**
  42 + * Set Connection
  43 + *
  44 + * @static
  45 + * @access public
  46 + * @return bool
  47 + * @param mixed $conn
  48 + */
  49 + function setConnection($conn)
  50 + {
  51 + if (is_a($conn, 'DB_Common')) {
  52 + $conn = $conn->dbh;
  53 + } elseif ( is_a($conn, 'MDB_Common') ||
  54 + is_a($conn, 'MDB2_Driver_Common')) {
  55 + $conn = $conn->connection;
  56 + }
  57 + if ($isResource = is_resource($conn)) {
  58 + $GLOBALS['_HTTP_Download_PgLOB_Connection'] = $conn;
  59 + }
  60 + return $isResource;
  61 + }
  62 +
  63 + /**
  64 + * Get Connection
  65 + *
  66 + * @static
  67 + * @access public
  68 + * @return resource
  69 + */
  70 + function getConnection()
  71 + {
  72 + if (is_resource($GLOBALS['_HTTP_Download_PgLOB_Connection'])) {
  73 + return $GLOBALS['_HTTP_Download_PgLOB_Connection'];
  74 + }
  75 + return null;
  76 + }
  77 +
  78 + /**
  79 + * Open
  80 + *
  81 + * @static
  82 + * @access public
  83 + * @return resource
  84 + * @param mixed $conn
  85 + * @param int $loid
  86 + * @param string $mode
  87 + */
  88 + function open($conn, $loid, $mode = 'rb')
  89 + {
  90 + HTTP_Download_PgLOB::setConnection($conn);
  91 + return fopen('pglob:///'. $loid, $mode);
  92 + }
  93 +
  94 + /**#@+
  95 + * Stream Interface Implementation
  96 + * @internal
  97 + */
  98 + var $ID = 0;
  99 + var $size = 0;
  100 + var $conn = null;
  101 + var $handle = null;
  102 +
  103 + function stream_open($path, $mode)
  104 + {
  105 + if (!$this->conn = HTTP_Download_PgLOB::getConnection()) {
  106 + return false;
  107 + }
  108 + if (!preg_match('/(\d+)/', $path, $matches)) {
  109 + return false;
  110 + }
  111 + $this->ID = $matches[1];
  112 +
  113 + if (!pg_query($this->conn, 'BEGIN')) {
  114 + return false;
  115 + }
  116 +
  117 + $this->handle = pg_lo_open($this->conn, $this->ID, $mode);
  118 + if (!is_resource($this->handle)) {
  119 + return false;
  120 + }
  121 +
  122 + // fetch size of lob
  123 + pg_lo_seek($this->handle, 0, PGSQL_SEEK_END);
  124 + $this->size = (int) pg_lo_tell($this->handle);
  125 + pg_lo_seek($this->handle, 0, PGSQL_SEEK_SET);
  126 +
  127 + return true;
  128 + }
  129 +
  130 + function stream_read($length)
  131 + {
  132 + return pg_lo_read($this->handle, $length);
  133 + }
  134 +
  135 + function stream_seek($offset, $whence = SEEK_SET)
  136 + {
  137 + return pg_lo_seek($this->handle, $offset, $whence);
  138 + }
  139 +
  140 + function stream_tell()
  141 + {
  142 + return pg_lo_tell($this->handle);
  143 + }
  144 +
  145 + function stream_eof()
  146 + {
  147 + return pg_lo_tell($this->handle) >= $this->size;
  148 + }
  149 +
  150 + function stream_flush()
  151 + {
  152 + return true;
  153 + }
  154 +
  155 + function stream_stat()
  156 + {
  157 + return array('size' => $this->size, 'ino' => $this->ID);
  158 + }
  159 +
  160 + function stream_write($data)
  161 + {
  162 + return pg_lo_write($this->handle, $data);
  163 + }
  164 +
  165 + function stream_close()
  166 + {
  167 + if (pg_lo_close($this->handle)) {
  168 + return pg_query($this->conn, 'COMMIT');
  169 + } else {
  170 + pg_query($this->conn ,'ROLLBACK');
  171 + return false;
  172 + }
  173 + }
  174 + /**#@-*/
  175 +}
  176 +
  177 +?>
... ...
thirdparty/pear/WebDAV/Header.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Header
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Header
  11 + * @author Wolfram Kriesing <wk@visionp.de>
  12 + * @author Davey Shafik <davey@php.net>
  13 + * @author Michael Wallner <mike@php.net>
  14 + * @copyright 2003-2005 The Authors
  15 + * @license BSD, revised
  16 + * @version CVS: $Id: Header.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  17 + * @link http://pear.php.net/package/HTTP_Header
  18 + */
  19 +
  20 +/**
  21 + * Requires HTTP
  22 + */
  23 +require_once 'HTTP.php';
  24 +
  25 +/**#@+
  26 + * Information Codes
  27 + */
  28 +define('HTTP_HEADER_STATUS_100', '100 Continue');
  29 +define('HTTP_HEADER_STATUS_101', '101 Switching Protocols');
  30 +define('HTTP_HEADER_STATUS_102', '102 Processing');
  31 +define('HTTP_HEADER_STATUS_INFORMATIONAL',1);
  32 +/**#@-*/
  33 +
  34 +/**#+
  35 + * Success Codes
  36 + */
  37 +define('HTTP_HEADER_STATUS_200', '200 OK');
  38 +define('HTTP_HEADER_STATUS_201', '201 Created');
  39 +define('HTTP_HEADER_STATUS_202', '202 Accepted');
  40 +define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information');
  41 +define('HTTP_HEADER_STATUS_204', '204 No Content');
  42 +define('HTTP_HEADER_STATUS_205', '205 Reset Content');
  43 +define('HTTP_HEADER_STATUS_206', '206 Partial Content');
  44 +define('HTTP_HEADER_STATUS_207', '207 Multi-Status');
  45 +define('HTTP_HEADER_STATUS_SUCCESSFUL',2);
  46 +/**#@-*/
  47 +
  48 +/**#@+
  49 + * Redirection Codes
  50 + */
  51 +define('HTTP_HEADER_STATUS_300', '300 Multiple Choices');
  52 +define('HTTP_HEADER_STATUS_301', '301 Moved Permanently');
  53 +define('HTTP_HEADER_STATUS_302', '302 Found');
  54 +define('HTTP_HEADER_STATUS_303', '303 See Other');
  55 +define('HTTP_HEADER_STATUS_304', '304 Not Modified');
  56 +define('HTTP_HEADER_STATUS_305', '305 Use Proxy');
  57 +define('HTTP_HEADER_STATUS_306', '306 (Unused)');
  58 +define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect');
  59 +define('HTTP_HEADER_STATUS_REDIRECT',3);
  60 +/**#@-*/
  61 +
  62 +/**#@+
  63 + * Error Codes
  64 + */
  65 +define('HTTP_HEADER_STATUS_400', '400 Bad Request');
  66 +define('HTTP_HEADER_STATUS_401', '401 Unauthorized');
  67 +define('HTTP_HEADER_STATUS_402', '402 Payment Granted');
  68 +define('HTTP_HEADER_STATUS_403', '403 Forbidden');
  69 +define('HTTP_HEADER_STATUS_404', '404 File Not Found');
  70 +define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed');
  71 +define('HTTP_HEADER_STATUS_406', '406 Not Acceptable');
  72 +define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required');
  73 +define('HTTP_HEADER_STATUS_408', '408 Request Time-out');
  74 +define('HTTP_HEADER_STATUS_409', '409 Conflict');
  75 +define('HTTP_HEADER_STATUS_410', '410 Gone');
  76 +define('HTTP_HEADER_STATUS_411', '411 Length Required');
  77 +define('HTTP_HEADER_STATUS_412', '412 Precondition Failed');
  78 +define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large');
  79 +define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large');
  80 +define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type');
  81 +define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable');
  82 +define('HTTP_HEADER_STATUS_417', '417 Expectation Failed');
  83 +define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity');
  84 +define('HTTP_HEADER_STATUS_423', '423 Locked');
  85 +define('HTTP_HEADER_STATUS_424', '424 Failed Dependency');
  86 +define('HTTP_HEADER_STATUS_CLIENT_ERROR',4);
  87 +/**#@-*/
  88 +
  89 +/**#@+
  90 + * Server Errors
  91 + */
  92 +define('HTTP_HEADER_STATUS_500', '500 Internal Server Error');
  93 +define('HTTP_HEADER_STATUS_501', '501 Not Implemented');
  94 +define('HTTP_HEADER_STATUS_502', '502 Bad Gateway');
  95 +define('HTTP_HEADER_STATUS_503', '503 Service Unavailable');
  96 +define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out');
  97 +define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported');
  98 +define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage');
  99 +define('HTTP_HEADER_STATUS_SERVER_ERROR',5);
  100 +/**#@-*/
  101 +
  102 +/**
  103 + * HTTP_Header
  104 + *
  105 + * @package HTTP_Header
  106 + * @category HTTP
  107 + * @access public
  108 + * @version $Revision: 6819 $
  109 + */
  110 +class HTTP_Header extends HTTP
  111 +{
  112 + /**
  113 + * Default Headers
  114 + *
  115 + * The values that are set as default, are the same as PHP sends by default.
  116 + *
  117 + * @var array
  118 + * @access private
  119 + */
  120 + var $_headers = array(
  121 + 'content-type' => 'text/html',
  122 + 'pragma' => 'no-cache',
  123 + 'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
  124 + );
  125 +
  126 + /**
  127 + * HTTP version
  128 + *
  129 + * @var string
  130 + * @access private
  131 + */
  132 + var $_httpVersion = '1.0';
  133 +
  134 + /**
  135 + * Constructor
  136 + *
  137 + * Sets HTTP version.
  138 + *
  139 + * @access public
  140 + * @return object HTTP_Header
  141 + */
  142 + function HTTP_Header()
  143 + {
  144 + if (isset($_SERVER['SERVER_PROTOCOL'])) {
  145 + $this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3));
  146 + }
  147 + }
  148 +
  149 + /**
  150 + * Set HTTP version
  151 + *
  152 + * @access public
  153 + * @return bool Returns true on success or false if version doesn't
  154 + * match 1.0 or 1.1 (note: 1 will result in 1.0)
  155 + * @param mixed $version HTTP version, either 1.0 or 1.1
  156 + */
  157 + function setHttpVersion($version)
  158 + {
  159 + $version = round((float) $version, 1);
  160 + if ($version < 1.0 || $version > 1.1) {
  161 + return false;
  162 + }
  163 + $this->_httpVersion = sprintf('%0.1f', $version);
  164 + return true;
  165 + }
  166 +
  167 + /**
  168 + * Get HTTP version
  169 + *
  170 + * @access public
  171 + * @return string
  172 + */
  173 + function getHttpVersion()
  174 + {
  175 + return $this->_httpVersion;
  176 + }
  177 +
  178 + /**
  179 + * Set Header
  180 + *
  181 + * The default value for the Last-Modified header will be current
  182 + * date and atime if $value is omitted.
  183 + *
  184 + * @access public
  185 + * @return bool Returns true on success or false if $key was empty or
  186 + * $value was not of an scalar type.
  187 + * @param string $key The name of the header.
  188 + * @param string $value The value of the header. (NULL to unset header)
  189 + */
  190 + function setHeader($key, $value = null)
  191 + {
  192 + if (empty($key) || (isset($value) && !is_scalar($value))) {
  193 + return false;
  194 + }
  195 +
  196 + $key = strToLower($key);
  197 + if ($key == 'last-modified') {
  198 + if (!isset($value)) {
  199 + $value = HTTP::Date(time());
  200 + } elseif (is_numeric($value)) {
  201 + $value = HTTP::Date($value);
  202 + }
  203 + }
  204 +
  205 + if (isset($value)) {
  206 + $this->_headers[$key] = $value;
  207 + } else {
  208 + unset($this->_headers[$key]);
  209 + }
  210 +
  211 + return true;
  212 + }
  213 +
  214 + /**
  215 + * Get Header
  216 + *
  217 + * If $key is omitted, all stored headers will be returned.
  218 + *
  219 + * @access public
  220 + * @return mixed Returns string value of the requested header,
  221 + * array values of all headers or false if header $key
  222 + * is not set.
  223 + * @param string $key The name of the header to fetch.
  224 + */
  225 + function getHeader($key = null)
  226 + {
  227 + if (!isset($key)) {
  228 + return $this->_headers;
  229 + }
  230 +
  231 + $key = strToLower($key);
  232 +
  233 + if (!isset($this->_headers[$key])) {
  234 + return false;
  235 + }
  236 +
  237 + return $this->_headers[$key];
  238 + }
  239 +
  240 + /**
  241 + * Send Headers
  242 + *
  243 + * Send out the header that you set via setHeader().
  244 + *
  245 + * @access public
  246 + * @return bool Returns true on success or false if headers are already
  247 + * sent.
  248 + * @param array $keys Headers to (not) send, see $include.
  249 + * @param array $include If true only $keys matching headers will be
  250 + * sent, if false only header not matching $keys will be
  251 + * sent.
  252 + */
  253 + function sendHeaders($keys = array(), $include = true)
  254 + {
  255 + if (headers_sent()) {
  256 + return false;
  257 + }
  258 +
  259 + if (count($keys)) {
  260 + array_change_key_case($keys, CASE_LOWER);
  261 + foreach ($this->_headers as $key => $value) {
  262 + if ($include ? in_array($key, $keys) : !in_array($key, $keys)) {
  263 + header($key .': '. $value);
  264 + }
  265 + }
  266 + } else {
  267 + foreach ($this->_headers as $header => $value) {
  268 + header($header .': '. $value);
  269 + }
  270 + }
  271 + return true;
  272 + }
  273 +
  274 + /**
  275 + * Send Satus Code
  276 + *
  277 + * Send out the given HTTP-Status code. Use this for example when you
  278 + * want to tell the client this page is cached, then you would call
  279 + * sendStatusCode(304).
  280 + *
  281 + * @see HTTP_Header_Cache::exitIfCached()
  282 + *
  283 + * @access public
  284 + * @return bool Returns true on success or false if headers are already
  285 + * sent.
  286 + * @param int $code The status code to send, i.e. 404, 304, 200, etc.
  287 + */
  288 + function sendStatusCode($code)
  289 + {
  290 + if (headers_sent()) {
  291 + return false;
  292 + }
  293 +
  294 + if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) {
  295 + $code = constant('HTTP_HEADER_STATUS_'. $code);
  296 + }
  297 +
  298 + if (strncasecmp(PHP_SAPI, 'cgi', 3)) {
  299 + header('HTTP/'. $this->_httpVersion .' '. $code);
  300 + } else {
  301 + header('Status: '. $code);
  302 + }
  303 + return true;
  304 + }
  305 +
  306 + /**
  307 + * Date to Timestamp
  308 + *
  309 + * Converts dates like
  310 + * Mon, 31 Mar 2003 15:26:34 GMT
  311 + * Tue, 15 Nov 1994 12:45:26 GMT
  312 + * into a timestamp, strtotime() didn't do it in older versions.
  313 + *
  314 + * @deprecated Use PHPs strtotime() instead.
  315 + * @access public
  316 + * @return mixed Returns int unix timestamp or false if the date doesn't
  317 + * seem to be a valid GMT date.
  318 + * @param string $date The GMT date.
  319 + */
  320 + function dateToTimestamp($date)
  321 + {
  322 + static $months = array(
  323 + null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
  324 + 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9,
  325 + 'Oct' => 10, 'Nov' => 11, 'Dec' => 12
  326 + );
  327 +
  328 + if (-1 < $timestamp = strToTime($date)) {
  329 + return $timestamp;
  330 + }
  331 +
  332 + if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~',
  333 + $date, $m)) {
  334 + return false;
  335 + }
  336 +
  337 + // [0] => Mon, 31 Mar 2003 15:42:55 GMT
  338 + // [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55
  339 + return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]);
  340 + }
  341 +
  342 + /**
  343 + * Redirect
  344 + *
  345 + * This function redirects the client. This is done by issuing a Location
  346 + * header and exiting. Additionally to HTTP::redirect() you can also add
  347 + * parameters to the url.
  348 + *
  349 + * If you dont need parameters to be added, simply use HTTP::redirect()
  350 + * otherwise use HTTP_Header::redirect().
  351 + *
  352 + * @see HTTP::redirect()
  353 + * @author Wolfram Kriesing <wk@visionp.de>
  354 + * @access public
  355 + * @return void
  356 + * @param string $url The URL to redirect to, if none is given it
  357 + * redirects to the current page.
  358 + * @param array $param Array of query string parameters to add; usually
  359 + * a set of key => value pairs; if an array entry consists
  360 + * only of an value it is used as key and the respective
  361 + * value is fetched from $GLOBALS[$value]
  362 + * @param bool $session Whether the session name/id should be added
  363 + */
  364 + function redirect($url = null, $param = array(), $session = false)
  365 + {
  366 + if (!isset($url)) {
  367 + $url = $_SERVER['PHP_SELF'];
  368 + }
  369 +
  370 + $qs = array();
  371 +
  372 + if ($session) {
  373 + $qs[] = session_name() .'='. session_id();
  374 + }
  375 +
  376 + if (is_array($param) && count($param)) {
  377 + if (count($param)) {
  378 + foreach ($param as $key => $val) {
  379 + if (is_string($key)) {
  380 + $qs[] = urlencode($key) .'='. urlencode($val);
  381 + } else {
  382 + $qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]);
  383 + }
  384 + }
  385 + }
  386 + }
  387 +
  388 + if ($qstr = implode('&', $qs)) {
  389 + $purl = parse_url($url);
  390 + $url .= (isset($purl['query']) ? '&' : '?') . $qstr;
  391 + }
  392 +
  393 + parent::redirect($url);
  394 + }
  395 +
  396 + /**#@+
  397 + * @author Davey Shafik <davey@php.net>
  398 + * @param int $http_code HTTP Code to check
  399 + * @access public
  400 + */
  401 +
  402 + /**
  403 + * Return HTTP Status Code Type
  404 + *
  405 + * @return int|false
  406 + */
  407 + function getStatusType($http_code)
  408 + {
  409 + if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) {
  410 + $type = substr($http_code,0,1);
  411 + switch ($type) {
  412 + case HTTP_HEADER_STATUS_INFORMATIONAL:
  413 + case HTTP_HEADER_STATUS_SUCCESSFUL:
  414 + case HTTP_HEADER_STATUS_REDIRECT:
  415 + case HTTP_HEADER_STATUS_CLIENT_ERROR:
  416 + case HTTP_HEADER_STATUS_SERVER_ERROR:
  417 + return $type;
  418 + break;
  419 + default:
  420 + return false;
  421 + break;
  422 + }
  423 + } else {
  424 + return false;
  425 + }
  426 + }
  427 +
  428 + /**
  429 + * Return Status Code Message
  430 + *
  431 + * @return string|false
  432 + */
  433 + function getStatusText($http_code)
  434 + {
  435 + if ($this->getStatusType($http_code)) {
  436 + if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) {
  437 + return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4);
  438 + } else {
  439 + return substr($http_code,4);
  440 + }
  441 + } else {
  442 + return false;
  443 + }
  444 + }
  445 +
  446 + /**
  447 + * Checks if HTTP Status code is Information (1xx)
  448 + *
  449 + * @return boolean
  450 + */
  451 + function isInformational($http_code)
  452 + {
  453 + if ($status_type = $this->getStatusType($http_code)) {
  454 + return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL;
  455 + } else {
  456 + return false;
  457 + }
  458 + }
  459 +
  460 + /**
  461 + * Checks if HTTP Status code is Successful (2xx)
  462 + *
  463 + * @return boolean
  464 + */
  465 + function isSuccessful($http_code)
  466 + {
  467 + if ($status_type = $this->getStatusType($http_code)) {
  468 + return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL;
  469 + } else {
  470 + return false;
  471 + }
  472 + }
  473 +
  474 + /**
  475 + * Checks if HTTP Status code is a Redirect (3xx)
  476 + *
  477 + * @return boolean
  478 + */
  479 + function isRedirect($http_code)
  480 + {
  481 + if ($status_type = $this->getStatusType($http_code)) {
  482 + return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT;
  483 + } else {
  484 + return false;
  485 + }
  486 + }
  487 +
  488 + /**
  489 + * Checks if HTTP Status code is a Client Error (4xx)
  490 + *
  491 + * @return boolean
  492 + */
  493 + function isClientError($http_code)
  494 + {
  495 + if ($status_type = $this->getStatusType($http_code)) {
  496 + return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR;
  497 + } else {
  498 + return false;
  499 + }
  500 + }
  501 +
  502 + /**
  503 + * Checks if HTTP Status code is Server Error (5xx)
  504 + *
  505 + * @return boolean
  506 + */
  507 + function isServerError($http_code)
  508 + {
  509 + if ($status_type = $this->getStatusType($http_code)) {
  510 + return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR;
  511 + } else {
  512 + return false;
  513 + }
  514 + }
  515 +
  516 + /**
  517 + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
  518 + *
  519 + * @return boolean
  520 + */
  521 + function isError($http_code)
  522 + {
  523 + if ($status_type = $this->getStatusType($http_code)) {
  524 + return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false;
  525 + } else {
  526 + return false;
  527 + }
  528 + }
  529 + /**#@-*/
  530 +}
  531 +?>
... ...
thirdparty/pear/WebDAV/Header/Cache.php 0 โ†’ 100644
  1 +<?php
  2 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3 +
  4 +/**
  5 + * HTTP::Header::Cache
  6 + *
  7 + * PHP versions 4 and 5
  8 + *
  9 + * @category HTTP
  10 + * @package HTTP_Header
  11 + * @author Wolfram Kriesing <wk@visionp.de>
  12 + * @author Michael Wallner <mike@php.net>
  13 + * @copyright 2003-2005 The Authors
  14 + * @license BSD, revised
  15 + * @version CVS: $Id: Cache.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  16 + * @link http://pear.php.net/package/HTTP_Header
  17 + */
  18 +
  19 +/**
  20 + * Requires HTTP_Header
  21 + */
  22 +require_once 'HTTP/Header.php';
  23 +
  24 +/**
  25 + * HTTP_Header_Cache
  26 + *
  27 + * This package provides methods to easier handle caching of HTTP pages. That
  28 + * means that the pages can be cached at the client (user agent or browser) and
  29 + * your application only needs to send "hey client you already have the pages".
  30 + *
  31 + * Which is done by sending the HTTP-Status "304 Not Modified", so that your
  32 + * application load and the network traffic can be reduced, since you only need
  33 + * to send the complete page once. This is really an advantage e.g. for
  34 + * generated style sheets, or simply pages that do only change rarely.
  35 + *
  36 + * Usage:
  37 + * <code>
  38 + * require_once 'HTTP/Header/Cache.php';
  39 + * $httpCache = new HTTP_Header_Cache(4, 'weeks');
  40 + * $httpCache->sendHeaders();
  41 + * // your code goes here
  42 + * </code>
  43 + *
  44 + * @package HTTP_Header
  45 + * @category HTTP
  46 + * @access public
  47 + * @version $Revision: 6819 $
  48 + */
  49 +class HTTP_Header_Cache extends HTTP_Header
  50 +{
  51 + /**
  52 + * Constructor
  53 + *
  54 + * Set the amount of time to cache.
  55 + *
  56 + * @access public
  57 + * @return object HTTP_Header_Cache
  58 + * @param int $expires
  59 + * @param string $unit
  60 + */
  61 + function HTTP_Header_Cache($expires = 0, $unit = 'seconds')
  62 + {
  63 + parent::HTTP_Header();
  64 + $this->setHeader('Pragma', 'cache');
  65 + $this->setHeader('Last-Modified', $this->getCacheStart());
  66 + $this->setHeader('Cache-Control', 'private, must-revalidate, max-age=0');
  67 +
  68 + if ($expires) {
  69 + if (!$this->isOlderThan($expires, $unit)) {
  70 + $this->exitCached();
  71 + }
  72 + $this->setHeader('Last-Modified', time());
  73 + }
  74 + }
  75 +
  76 + /**
  77 + * Get Cache Start
  78 + *
  79 + * Returns the unix timestamp of the If-Modified-Since HTTP header or the
  80 + * current time if the header was not sent by the client.
  81 + *
  82 + * @access public
  83 + * @return int unix timestamp
  84 + */
  85 + function getCacheStart()
  86 + {
  87 + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$this->isPost()) {
  88 + return strtotime(current($array = explode(';',
  89 + $_SERVER['HTTP_IF_MODIFIED_SINCE'])));
  90 + }
  91 + return time();
  92 + }
  93 +
  94 + /**
  95 + * Is Older Than
  96 + *
  97 + * You can call it like this:
  98 + * <code>
  99 + * $httpCache->isOlderThan(1, 'day');
  100 + * $httpCache->isOlderThan(47, 'days');
  101 + *
  102 + * $httpCache->isOlderThan(1, 'week');
  103 + * $httpCache->isOlderThan(3, 'weeks');
  104 + *
  105 + * $httpCache->isOlderThan(1, 'hour');
  106 + * $httpCache->isOlderThan(5, 'hours');
  107 + *
  108 + * $httpCache->isOlderThan(1, 'minute');
  109 + * $httpCache->isOlderThan(15, 'minutes');
  110 + *
  111 + * $httpCache->isOlderThan(1, 'second');
  112 + * $httpCache->isOlderThan(15);
  113 + * </code>
  114 + *
  115 + * If you specify something greater than "weeks" as time untit, it just
  116 + * works approximatly, because a month is taken to consist of 4.3 weeks.
  117 + *
  118 + * @access public
  119 + * @return bool Returns true if requested page is older than specified.
  120 + * @param int $time The amount of time.
  121 + * @param string $unit The unit of the time amount - (year[s], month[s],
  122 + * week[s], day[s], hour[s], minute[s], second[s]).
  123 + */
  124 + function isOlderThan($time = 0, $unit = 'seconds')
  125 + {
  126 + if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || $this->isPost()) {
  127 + return true;
  128 + }
  129 + if (!$time) {
  130 + return false;
  131 + }
  132 +
  133 + switch (strtolower($unit))
  134 + {
  135 + case 'year':
  136 + case 'years':
  137 + $time *= 12;
  138 + case 'month':
  139 + case 'months':
  140 + $time *= 4.3;
  141 + case 'week':
  142 + case 'weeks':
  143 + $time *= 7;
  144 + case 'day':
  145 + case 'days':
  146 + $time *= 24;
  147 + case 'hour':
  148 + case 'hours':
  149 + $time *= 60;
  150 + case 'minute':
  151 + case 'minutes':
  152 + $time *= 60;
  153 + }
  154 +
  155 + return (time() - $this->getCacheStart()) > $time;
  156 + }
  157 +
  158 + /**
  159 + * Is Cached
  160 + *
  161 + * Check whether we can consider to be cached on the client side.
  162 + *
  163 + * @access public
  164 + * @return bool Whether the page/resource is considered to be cached.
  165 + * @param int $lastModified Unix timestamp of last modification.
  166 + */
  167 + function isCached($lastModified = 0)
  168 + {
  169 + if ($this->isPost()) {
  170 + return false;
  171 + }
  172 + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !$lastModified) {
  173 + return true;
  174 + }
  175 + if (!$seconds = time() - $lastModified) {
  176 + return false;
  177 + }
  178 + return !$this->isOlderThan($seconds);
  179 + }
  180 +
  181 + /**
  182 + * Is Post
  183 + *
  184 + * Check if request method is "POST".
  185 + *
  186 + * @access public
  187 + * @return bool
  188 + */
  189 + function isPost()
  190 + {
  191 + return isset($_SERVER['REQUEST_METHOD']) and
  192 + 'POST' == $_SERVER['REQUEST_METHOD'];
  193 + }
  194 +
  195 + /**
  196 + * Exit If Cached
  197 + *
  198 + * Exit with "HTTP 304 Not Modified" if we consider to be cached.
  199 + *
  200 + * @access public
  201 + * @return void
  202 + * @param int $lastModified Unix timestamp of last modification.
  203 + */
  204 + function exitIfCached($lastModified = 0)
  205 + {
  206 + if ($this->isCached($lastModified)) {
  207 + $this->exitCached();
  208 + }
  209 + }
  210 +
  211 + /**
  212 + * Exit Cached
  213 + *
  214 + * Exit with "HTTP 304 Not Modified".
  215 + *
  216 + * @access public
  217 + * @return void
  218 + */
  219 + function exitCached()
  220 + {
  221 + $this->sendHeaders();
  222 + $this->sendStatusCode(304);
  223 + exit;
  224 + }
  225 +
  226 + /**
  227 + * Set Last Modified
  228 + *
  229 + * @access public
  230 + * @return void
  231 + * @param int $lastModified The unix timestamp of last modification.
  232 + */
  233 + function setLastModified($lastModified = null)
  234 + {
  235 + $this->setHeader('Last-Modified', $lastModified);
  236 + }
  237 +}
  238 +?>
... ...
thirdparty/pear/WebDAV/Request.php 0 โ†’ 100644
  1 +<?php
  2 +// +-----------------------------------------------------------------------+
  3 +// | Copyright (c) 2002-2003, Richard Heyes |
  4 +// | All rights reserved. |
  5 +// | |
  6 +// | Redistribution and use in source and binary forms, with or without |
  7 +// | modification, are permitted provided that the following conditions |
  8 +// | are met: |
  9 +// | |
  10 +// | o Redistributions of source code must retain the above copyright |
  11 +// | notice, this list of conditions and the following disclaimer. |
  12 +// | o Redistributions in binary form must reproduce the above copyright |
  13 +// | notice, this list of conditions and the following disclaimer in the |
  14 +// | documentation and/or other materials provided with the distribution.|
  15 +// | o The names of the authors may not be used to endorse or promote |
  16 +// | products derived from this software without specific prior written |
  17 +// | permission. |
  18 +// | |
  19 +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  20 +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  21 +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22 +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
  23 +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24 +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
  25 +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26 +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27 +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
  28 +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29 +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
  30 +// | |
  31 +// +-----------------------------------------------------------------------+
  32 +// | Author: Richard Heyes <richard@phpguru.org> |
  33 +// +-----------------------------------------------------------------------+
  34 +//
  35 +// $Id: Request.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  36 +//
  37 +// HTTP_Request Class
  38 +//
  39 +// Simple example, (Fetches yahoo.com and displays it):
  40 +//
  41 +// $a = &new HTTP_Request('http://www.yahoo.com/');
  42 +// $a->sendRequest();
  43 +// echo $a->getResponseBody();
  44 +//
  45 +
  46 +require_once 'PEAR.php';
  47 +require_once 'Net/Socket.php';
  48 +require_once 'Net/URL.php';
  49 +
  50 +define('HTTP_REQUEST_METHOD_GET', 'GET', true);
  51 +define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
  52 +define('HTTP_REQUEST_METHOD_POST', 'POST', true);
  53 +define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
  54 +define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
  55 +define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
  56 +define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
  57 +
  58 +define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
  59 +define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
  60 +
  61 +class HTTP_Request {
  62 +
  63 + /**
  64 + * Instance of Net_URL
  65 + * @var object Net_URL
  66 + */
  67 + var $_url;
  68 +
  69 + /**
  70 + * Type of request
  71 + * @var string
  72 + */
  73 + var $_method;
  74 +
  75 + /**
  76 + * HTTP Version
  77 + * @var string
  78 + */
  79 + var $_http;
  80 +
  81 + /**
  82 + * Request headers
  83 + * @var array
  84 + */
  85 + var $_requestHeaders;
  86 +
  87 + /**
  88 + * Basic Auth Username
  89 + * @var string
  90 + */
  91 + var $_user;
  92 +
  93 + /**
  94 + * Basic Auth Password
  95 + * @var string
  96 + */
  97 + var $_pass;
  98 +
  99 + /**
  100 + * Socket object
  101 + * @var object Net_Socket
  102 + */
  103 + var $_sock;
  104 +
  105 + /**
  106 + * Proxy server
  107 + * @var string
  108 + */
  109 + var $_proxy_host;
  110 +
  111 + /**
  112 + * Proxy port
  113 + * @var integer
  114 + */
  115 + var $_proxy_port;
  116 +
  117 + /**
  118 + * Proxy username
  119 + * @var string
  120 + */
  121 + var $_proxy_user;
  122 +
  123 + /**
  124 + * Proxy password
  125 + * @var string
  126 + */
  127 + var $_proxy_pass;
  128 +
  129 + /**
  130 + * Post data
  131 + * @var array
  132 + */
  133 + var $_postData;
  134 +
  135 + /**
  136 + * Request body
  137 + * @var string
  138 + */
  139 + var $_body;
  140 +
  141 + /**
  142 + * A list of methods that MUST NOT have a request body, per RFC 2616
  143 + * @var array
  144 + */
  145 + var $_bodyDisallowed = array('TRACE');
  146 +
  147 + /**
  148 + * Files to post
  149 + * @var array
  150 + */
  151 + var $_postFiles = array();
  152 +
  153 + /**
  154 + * Connection timeout.
  155 + * @var float
  156 + */
  157 + var $_timeout;
  158 +
  159 + /**
  160 + * HTTP_Response object
  161 + * @var object HTTP_Response
  162 + */
  163 + var $_response;
  164 +
  165 + /**
  166 + * Whether to allow redirects
  167 + * @var boolean
  168 + */
  169 + var $_allowRedirects;
  170 +
  171 + /**
  172 + * Maximum redirects allowed
  173 + * @var integer
  174 + */
  175 + var $_maxRedirects;
  176 +
  177 + /**
  178 + * Current number of redirects
  179 + * @var integer
  180 + */
  181 + var $_redirects;
  182 +
  183 + /**
  184 + * Whether to append brackets [] to array variables
  185 + * @var bool
  186 + */
  187 + var $_useBrackets = true;
  188 +
  189 + /**
  190 + * Attached listeners
  191 + * @var array
  192 + */
  193 + var $_listeners = array();
  194 +
  195 + /**
  196 + * Whether to save response body in response object property
  197 + * @var bool
  198 + */
  199 + var $_saveBody = true;
  200 +
  201 + /**
  202 + * Timeout for reading from socket (array(seconds, microseconds))
  203 + * @var array
  204 + */
  205 + var $_readTimeout = null;
  206 +
  207 + /**
  208 + * Options to pass to Net_Socket::connect. See stream_context_create
  209 + * @var array
  210 + */
  211 + var $_socketOptions = null;
  212 +
  213 + /**
  214 + * Constructor
  215 + *
  216 + * Sets up the object
  217 + * @param string The url to fetch/access
  218 + * @param array Associative array of parameters which can have the following keys:
  219 + * <ul>
  220 + * <li>method - Method to use, GET, POST etc (string)</li>
  221 + * <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
  222 + * <li>user - Basic Auth username (string)</li>
  223 + * <li>pass - Basic Auth password (string)</li>
  224 + * <li>proxy_host - Proxy server host (string)</li>
  225 + * <li>proxy_port - Proxy server port (integer)</li>
  226 + * <li>proxy_user - Proxy auth username (string)</li>
  227 + * <li>proxy_pass - Proxy auth password (string)</li>
  228 + * <li>timeout - Connection timeout in seconds (float)</li>
  229 + * <li>allowRedirects - Whether to follow redirects or not (bool)</li>
  230 + * <li>maxRedirects - Max number of redirects to follow (integer)</li>
  231 + * <li>useBrackets - Whether to append [] to array variable names (bool)</li>
  232 + * <li>saveBody - Whether to save response body in response object property (bool)</li>
  233 + * <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
  234 + * <li>socketOptions - Options to pass to Net_Socket object (array)</li>
  235 + * </ul>
  236 + * @access public
  237 + */
  238 + function HTTP_Request($url = '', $params = array())
  239 + {
  240 + $this->_sock = &new Net_Socket();
  241 + $this->_method = HTTP_REQUEST_METHOD_GET;
  242 + $this->_http = HTTP_REQUEST_HTTP_VER_1_1;
  243 + $this->_requestHeaders = array();
  244 + $this->_postData = array();
  245 + $this->_body = null;
  246 +
  247 + $this->_user = null;
  248 + $this->_pass = null;
  249 +
  250 + $this->_proxy_host = null;
  251 + $this->_proxy_port = null;
  252 + $this->_proxy_user = null;
  253 + $this->_proxy_pass = null;
  254 +
  255 + $this->_allowRedirects = false;
  256 + $this->_maxRedirects = 3;
  257 + $this->_redirects = 0;
  258 +
  259 + $this->_timeout = null;
  260 + $this->_response = null;
  261 +
  262 + foreach ($params as $key => $value) {
  263 + $this->{'_' . $key} = $value;
  264 + }
  265 +
  266 + if (!empty($url)) {
  267 + $this->setURL($url);
  268 + }
  269 +
  270 + // Default useragent
  271 + $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
  272 +
  273 + // Make sure keepalives dont knobble us
  274 + $this->addHeader('Connection', 'close');
  275 +
  276 + // Basic authentication
  277 + if (!empty($this->_user)) {
  278 + $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
  279 + }
  280 +
  281 + // Use gzip encoding if possible
  282 + // Avoid gzip encoding if using multibyte functions (see #1781)
  283 + if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib') &&
  284 + 0 == (2 & ini_get('mbstring.func_overload'))) {
  285 +
  286 + $this->addHeader('Accept-Encoding', 'gzip');
  287 + }
  288 + }
  289 +
  290 + /**
  291 + * Generates a Host header for HTTP/1.1 requests
  292 + *
  293 + * @access private
  294 + * @return string
  295 + */
  296 + function _generateHostHeader()
  297 + {
  298 + if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
  299 + $host = $this->_url->host . ':' . $this->_url->port;
  300 +
  301 + } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
  302 + $host = $this->_url->host . ':' . $this->_url->port;
  303 +
  304 + } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
  305 + $host = $this->_url->host . ':' . $this->_url->port;
  306 +
  307 + } else {
  308 + $host = $this->_url->host;
  309 + }
  310 +
  311 + return $host;
  312 + }
  313 +
  314 + /**
  315 + * Resets the object to its initial state (DEPRECATED).
  316 + * Takes the same parameters as the constructor.
  317 + *
  318 + * @param string $url The url to be requested
  319 + * @param array $params Associative array of parameters
  320 + * (see constructor for details)
  321 + * @access public
  322 + * @deprecated deprecated since 1.2, call the constructor if this is necessary
  323 + */
  324 + function reset($url, $params = array())
  325 + {
  326 + $this->HTTP_Request($url, $params);
  327 + }
  328 +
  329 + /**
  330 + * Sets the URL to be requested
  331 + *
  332 + * @param string The url to be requested
  333 + * @access public
  334 + */
  335 + function setURL($url)
  336 + {
  337 + $this->_url = &new Net_URL($url, $this->_useBrackets);
  338 +
  339 + if (!empty($this->_url->user) || !empty($this->_url->pass)) {
  340 + $this->setBasicAuth($this->_url->user, $this->_url->pass);
  341 + }
  342 +
  343 + if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
  344 + $this->addHeader('Host', $this->_generateHostHeader());
  345 + }
  346 + }
  347 +
  348 + /**
  349 + * Sets a proxy to be used
  350 + *
  351 + * @param string Proxy host
  352 + * @param int Proxy port
  353 + * @param string Proxy username
  354 + * @param string Proxy password
  355 + * @access public
  356 + */
  357 + function setProxy($host, $port = 8080, $user = null, $pass = null)
  358 + {
  359 + $this->_proxy_host = $host;
  360 + $this->_proxy_port = $port;
  361 + $this->_proxy_user = $user;
  362 + $this->_proxy_pass = $pass;
  363 +
  364 + if (!empty($user)) {
  365 + $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
  366 + }
  367 + }
  368 +
  369 + /**
  370 + * Sets basic authentication parameters
  371 + *
  372 + * @param string Username
  373 + * @param string Password
  374 + */
  375 + function setBasicAuth($user, $pass)
  376 + {
  377 + $this->_user = $user;
  378 + $this->_pass = $pass;
  379 +
  380 + $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
  381 + }
  382 +
  383 + /**
  384 + * Sets the method to be used, GET, POST etc.
  385 + *
  386 + * @param string Method to use. Use the defined constants for this
  387 + * @access public
  388 + */
  389 + function setMethod($method)
  390 + {
  391 + $this->_method = $method;
  392 + }
  393 +
  394 + /**
  395 + * Sets the HTTP version to use, 1.0 or 1.1
  396 + *
  397 + * @param string Version to use. Use the defined constants for this
  398 + * @access public
  399 + */
  400 + function setHttpVer($http)
  401 + {
  402 + $this->_http = $http;
  403 + }
  404 +
  405 + /**
  406 + * Adds a request header
  407 + *
  408 + * @param string Header name
  409 + * @param string Header value
  410 + * @access public
  411 + */
  412 + function addHeader($name, $value)
  413 + {
  414 + $this->_requestHeaders[strtolower($name)] = $value;
  415 + }
  416 +
  417 + /**
  418 + * Removes a request header
  419 + *
  420 + * @param string Header name to remove
  421 + * @access public
  422 + */
  423 + function removeHeader($name)
  424 + {
  425 + if (isset($this->_requestHeaders[strtolower($name)])) {
  426 + unset($this->_requestHeaders[strtolower($name)]);
  427 + }
  428 + }
  429 +
  430 + /**
  431 + * Adds a querystring parameter
  432 + *
  433 + * @param string Querystring parameter name
  434 + * @param string Querystring parameter value
  435 + * @param bool Whether the value is already urlencoded or not, default = not
  436 + * @access public
  437 + */
  438 + function addQueryString($name, $value, $preencoded = false)
  439 + {
  440 + $this->_url->addQueryString($name, $value, $preencoded);
  441 + }
  442 +
  443 + /**
  444 + * Sets the querystring to literally what you supply
  445 + *
  446 + * @param string The querystring data. Should be of the format foo=bar&x=y etc
  447 + * @param bool Whether data is already urlencoded or not, default = already encoded
  448 + * @access public
  449 + */
  450 + function addRawQueryString($querystring, $preencoded = true)
  451 + {
  452 + $this->_url->addRawQueryString($querystring, $preencoded);
  453 + }
  454 +
  455 + /**
  456 + * Adds postdata items
  457 + *
  458 + * @param string Post data name
  459 + * @param string Post data value
  460 + * @param bool Whether data is already urlencoded or not, default = not
  461 + * @access public
  462 + */
  463 + function addPostData($name, $value, $preencoded = false)
  464 + {
  465 + if ($preencoded) {
  466 + $this->_postData[$name] = $value;
  467 + } else {
  468 + $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
  469 + }
  470 + }
  471 +
  472 + /**
  473 + * Recursively applies the callback function to the value
  474 + *
  475 + * @param mixed Callback function
  476 + * @param mixed Value to process
  477 + * @access private
  478 + * @return mixed Processed value
  479 + */
  480 + function _arrayMapRecursive($callback, $value)
  481 + {
  482 + if (!is_array($value)) {
  483 + return call_user_func($callback, $value);
  484 + } else {
  485 + $map = array();
  486 + foreach ($value as $k => $v) {
  487 + $map[$k] = $this->_arrayMapRecursive($callback, $v);
  488 + }
  489 + return $map;
  490 + }
  491 + }
  492 +
  493 + /**
  494 + * Adds a file to upload
  495 + *
  496 + * This also changes content-type to 'multipart/form-data' for proper upload
  497 + *
  498 + * @access public
  499 + * @param string name of file-upload field
  500 + * @param mixed file name(s)
  501 + * @param mixed content-type(s) of file(s) being uploaded
  502 + * @return bool true on success
  503 + * @throws PEAR_Error
  504 + */
  505 + function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
  506 + {
  507 + if (!is_array($fileName) && !is_readable($fileName)) {
  508 + return PEAR::raiseError("File '{$fileName}' is not readable");
  509 + } elseif (is_array($fileName)) {
  510 + foreach ($fileName as $name) {
  511 + if (!is_readable($name)) {
  512 + return PEAR::raiseError("File '{$name}' is not readable");
  513 + }
  514 + }
  515 + }
  516 + $this->addHeader('Content-Type', 'multipart/form-data');
  517 + $this->_postFiles[$inputName] = array(
  518 + 'name' => $fileName,
  519 + 'type' => $contentType
  520 + );
  521 + return true;
  522 + }
  523 +
  524 + /**
  525 + * Adds raw postdata (DEPRECATED)
  526 + *
  527 + * @param string The data
  528 + * @param bool Whether data is preencoded or not, default = already encoded
  529 + * @access public
  530 + * @deprecated deprecated since 1.3.0, method addBody() should be used instead
  531 + */
  532 + function addRawPostData($postdata, $preencoded = true)
  533 + {
  534 + $this->_body = $preencoded ? $postdata : urlencode($postdata);
  535 + }
  536 +
  537 + /**
  538 + * Sets the request body (for POST, PUT and similar requests)
  539 + *
  540 + * @param string Request body
  541 + * @access public
  542 + */
  543 + function setBody($body)
  544 + {
  545 + $this->_body = $body;
  546 + }
  547 +
  548 + /**
  549 + * Clears any postdata that has been added (DEPRECATED).
  550 + *
  551 + * Useful for multiple request scenarios.
  552 + *
  553 + * @access public
  554 + * @deprecated deprecated since 1.2
  555 + */
  556 + function clearPostData()
  557 + {
  558 + $this->_postData = null;
  559 + }
  560 +
  561 + /**
  562 + * Appends a cookie to "Cookie:" header
  563 + *
  564 + * @param string $name cookie name
  565 + * @param string $value cookie value
  566 + * @access public
  567 + */
  568 + function addCookie($name, $value)
  569 + {
  570 + $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
  571 + $this->addHeader('Cookie', $cookies . $name . '=' . $value);
  572 + }
  573 +
  574 + /**
  575 + * Clears any cookies that have been added (DEPRECATED).
  576 + *
  577 + * Useful for multiple request scenarios
  578 + *
  579 + * @access public
  580 + * @deprecated deprecated since 1.2
  581 + */
  582 + function clearCookies()
  583 + {
  584 + $this->removeHeader('Cookie');
  585 + }
  586 +
  587 + /**
  588 + * Sends the request
  589 + *
  590 + * @access public
  591 + * @param bool Whether to store response body in Response object property,
  592 + * set this to false if downloading a LARGE file and using a Listener
  593 + * @return mixed PEAR error on error, true otherwise
  594 + */
  595 + function sendRequest($saveBody = true)
  596 + {
  597 + if (!is_a($this->_url, 'Net_URL')) {
  598 + return PEAR::raiseError('No URL given.');
  599 + }
  600 +
  601 + $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
  602 + $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
  603 +
  604 + // 4.3.0 supports SSL connections using OpenSSL. The function test determines
  605 + // we running on at least 4.3.0
  606 + if (strcasecmp($this->_url->protocol, 'https') == 0 AND function_exists('file_get_contents') AND extension_loaded('openssl')) {
  607 + if (isset($this->_proxy_host)) {
  608 + return PEAR::raiseError('HTTPS proxies are not supported.');
  609 + }
  610 + $host = 'ssl://' . $host;
  611 + }
  612 +
  613 + // magic quotes may fuck up file uploads and chunked response processing
  614 + $magicQuotes = ini_get('magic_quotes_runtime');
  615 + ini_set('magic_quotes_runtime', false);
  616 +
  617 + // If this is a second request, we may get away without
  618 + // re-connecting if they're on the same server
  619 + $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
  620 + PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
  621 +
  622 + if (!PEAR::isError($err)) {
  623 + if (!empty($this->_readTimeout)) {
  624 + $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
  625 + }
  626 +
  627 + $this->_notify('sentRequest');
  628 +
  629 + // Read the response
  630 + $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
  631 + $err = $this->_response->process($this->_saveBody && $saveBody);
  632 + }
  633 +
  634 + ini_set('magic_quotes_runtime', $magicQuotes);
  635 +
  636 + if (PEAR::isError($err)) {
  637 + return $err;
  638 + }
  639 +
  640 +
  641 + // Check for redirection
  642 + if ( $this->_allowRedirects
  643 + AND $this->_redirects <= $this->_maxRedirects
  644 + AND $this->getResponseCode() > 300
  645 + AND $this->getResponseCode() < 399
  646 + AND !empty($this->_response->_headers['location'])) {
  647 +
  648 +
  649 + $redirect = $this->_response->_headers['location'];
  650 +
  651 + // Absolute URL
  652 + if (preg_match('/^https?:\/\//i', $redirect)) {
  653 + $this->_url = &new Net_URL($redirect);
  654 + $this->addHeader('Host', $this->_generateHostHeader());
  655 + // Absolute path
  656 + } elseif ($redirect{0} == '/') {
  657 + $this->_url->path = $redirect;
  658 +
  659 + // Relative path
  660 + } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
  661 + if (substr($this->_url->path, -1) == '/') {
  662 + $redirect = $this->_url->path . $redirect;
  663 + } else {
  664 + $redirect = dirname($this->_url->path) . '/' . $redirect;
  665 + }
  666 + $redirect = Net_URL::resolvePath($redirect);
  667 + $this->_url->path = $redirect;
  668 +
  669 + // Filename, no path
  670 + } else {
  671 + if (substr($this->_url->path, -1) == '/') {
  672 + $redirect = $this->_url->path . $redirect;
  673 + } else {
  674 + $redirect = dirname($this->_url->path) . '/' . $redirect;
  675 + }
  676 + $this->_url->path = $redirect;
  677 + }
  678 +
  679 + $this->_redirects++;
  680 + return $this->sendRequest($saveBody);
  681 +
  682 + // Too many redirects
  683 + } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
  684 + return PEAR::raiseError('Too many redirects');
  685 + }
  686 +
  687 + $this->_sock->disconnect();
  688 +
  689 + return true;
  690 + }
  691 +
  692 + /**
  693 + * Returns the response code
  694 + *
  695 + * @access public
  696 + * @return mixed Response code, false if not set
  697 + */
  698 + function getResponseCode()
  699 + {
  700 + return isset($this->_response->_code) ? $this->_response->_code : false;
  701 + }
  702 +
  703 + /**
  704 + * Returns either the named header or all if no name given
  705 + *
  706 + * @access public
  707 + * @param string The header name to return, do not set to get all headers
  708 + * @return mixed either the value of $headername (false if header is not present)
  709 + * or an array of all headers
  710 + */
  711 + function getResponseHeader($headername = null)
  712 + {
  713 + if (!isset($headername)) {
  714 + return isset($this->_response->_headers)? $this->_response->_headers: array();
  715 + } else {
  716 + $headername = strtolower($headername);
  717 + return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
  718 + }
  719 + }
  720 +
  721 + /**
  722 + * Returns the body of the response
  723 + *
  724 + * @access public
  725 + * @return mixed response body, false if not set
  726 + */
  727 + function getResponseBody()
  728 + {
  729 + return isset($this->_response->_body) ? $this->_response->_body : false;
  730 + }
  731 +
  732 + /**
  733 + * Returns cookies set in response
  734 + *
  735 + * @access public
  736 + * @return mixed array of response cookies, false if none are present
  737 + */
  738 + function getResponseCookies()
  739 + {
  740 + return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
  741 + }
  742 +
  743 + /**
  744 + * Builds the request string
  745 + *
  746 + * @access private
  747 + * @return string The request string
  748 + */
  749 + function _buildRequest()
  750 + {
  751 + $separator = ini_get('arg_separator.output');
  752 + ini_set('arg_separator.output', '&');
  753 + $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
  754 + ini_set('arg_separator.output', $separator);
  755 +
  756 + $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
  757 + $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
  758 + $path = (empty($this->_url->path)? '/': $this->_url->path) . $querystring;
  759 + $url = $host . $port . $path;
  760 +
  761 + $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
  762 +
  763 + if (in_array($this->_method, $this->_bodyDisallowed) ||
  764 + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body)) ||
  765 + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_postData) && empty($this->_postFiles))) {
  766 +
  767 + $this->removeHeader('Content-Type');
  768 + } else {
  769 + if (empty($this->_requestHeaders['content-type'])) {
  770 + // Add default content-type
  771 + $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
  772 + } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
  773 + $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
  774 + $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
  775 + }
  776 + }
  777 +
  778 + // Request Headers
  779 + if (!empty($this->_requestHeaders)) {
  780 + foreach ($this->_requestHeaders as $name => $value) {
  781 + $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
  782 + $request .= $canonicalName . ': ' . $value . "\r\n";
  783 + }
  784 + }
  785 +
  786 + // No post data or wrong method, so simply add a final CRLF
  787 + if (in_array($this->_method, $this->_bodyDisallowed) ||
  788 + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body))) {
  789 +
  790 + $request .= "\r\n";
  791 +
  792 + // Post data if it's an array
  793 + } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
  794 + (!empty($this->_postData) || !empty($this->_postFiles))) {
  795 +
  796 + // "normal" POST request
  797 + if (!isset($boundary)) {
  798 + $postdata = implode('&', array_map(
  799 + create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
  800 + $this->_flattenArray('', $this->_postData)
  801 + ));
  802 +
  803 + // multipart request, probably with file uploads
  804 + } else {
  805 + $postdata = '';
  806 + if (!empty($this->_postData)) {
  807 + $flatData = $this->_flattenArray('', $this->_postData);
  808 + foreach ($flatData as $item) {
  809 + $postdata .= '--' . $boundary . "\r\n";
  810 + $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
  811 + $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
  812 + }
  813 + }
  814 + foreach ($this->_postFiles as $name => $value) {
  815 + if (is_array($value['name'])) {
  816 + $varname = $name . ($this->_useBrackets? '[]': '');
  817 + } else {
  818 + $varname = $name;
  819 + $value['name'] = array($value['name']);
  820 + }
  821 + foreach ($value['name'] as $key => $filename) {
  822 + $fp = fopen($filename, 'r');
  823 + $data = fread($fp, filesize($filename));
  824 + fclose($fp);
  825 + $basename = basename($filename);
  826 + $type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
  827 +
  828 + $postdata .= '--' . $boundary . "\r\n";
  829 + $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
  830 + $postdata .= "\r\nContent-Type: " . $type;
  831 + $postdata .= "\r\n\r\n" . $data . "\r\n";
  832 + }
  833 + }
  834 + $postdata .= '--' . $boundary . "--\r\n";
  835 + }
  836 + $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n";
  837 + $request .= $postdata;
  838 +
  839 + // Explicitly set request body
  840 + } elseif (!empty($this->_body)) {
  841 +
  842 + $request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n";
  843 + $request .= $this->_body;
  844 + }
  845 +
  846 + return $request;
  847 + }
  848 +
  849 + /**
  850 + * Helper function to change the (probably multidimensional) associative array
  851 + * into the simple one.
  852 + *
  853 + * @param string name for item
  854 + * @param mixed item's values
  855 + * @return array array with the following items: array('item name', 'item value');
  856 + */
  857 + function _flattenArray($name, $values)
  858 + {
  859 + if (!is_array($values)) {
  860 + return array(array($name, $values));
  861 + } else {
  862 + $ret = array();
  863 + foreach ($values as $k => $v) {
  864 + if (empty($name)) {
  865 + $newName = $k;
  866 + } elseif ($this->_useBrackets) {
  867 + $newName = $name . '[' . $k . ']';
  868 + } else {
  869 + $newName = $name;
  870 + }
  871 + $ret = array_merge($ret, $this->_flattenArray($newName, $v));
  872 + }
  873 + return $ret;
  874 + }
  875 + }
  876 +
  877 +
  878 + /**
  879 + * Adds a Listener to the list of listeners that are notified of
  880 + * the object's events
  881 + *
  882 + * @param object HTTP_Request_Listener instance to attach
  883 + * @return boolean whether the listener was successfully attached
  884 + * @access public
  885 + */
  886 + function attach(&$listener)
  887 + {
  888 + if (!is_a($listener, 'HTTP_Request_Listener')) {
  889 + return false;
  890 + }
  891 + $this->_listeners[$listener->getId()] =& $listener;
  892 + return true;
  893 + }
  894 +
  895 +
  896 + /**
  897 + * Removes a Listener from the list of listeners
  898 + *
  899 + * @param object HTTP_Request_Listener instance to detach
  900 + * @return boolean whether the listener was successfully detached
  901 + * @access public
  902 + */
  903 + function detach(&$listener)
  904 + {
  905 + if (!is_a($listener, 'HTTP_Request_Listener') ||
  906 + !isset($this->_listeners[$listener->getId()])) {
  907 + return false;
  908 + }
  909 + unset($this->_listeners[$listener->getId()]);
  910 + return true;
  911 + }
  912 +
  913 +
  914 + /**
  915 + * Notifies all registered listeners of an event.
  916 + *
  917 + * Events sent by HTTP_Request object
  918 + * 'sentRequest': after the request was sent
  919 + * Events sent by HTTP_Response object
  920 + * 'gotHeaders': after receiving response headers (headers are passed in $data)
  921 + * 'tick': on receiving a part of response body (the part is passed in $data)
  922 + * 'gzTick': on receiving a gzip-encoded part of response body (ditto)
  923 + * 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
  924 + *
  925 + * @param string Event name
  926 + * @param mixed Additional data
  927 + * @access private
  928 + */
  929 + function _notify($event, $data = null)
  930 + {
  931 + foreach (array_keys($this->_listeners) as $id) {
  932 + $this->_listeners[$id]->update($this, $event, $data);
  933 + }
  934 + }
  935 +}
  936 +
  937 +
  938 +/**
  939 +* Response class to complement the Request class
  940 +*/
  941 +class HTTP_Response
  942 +{
  943 + /**
  944 + * Socket object
  945 + * @var object
  946 + */
  947 + var $_sock;
  948 +
  949 + /**
  950 + * Protocol
  951 + * @var string
  952 + */
  953 + var $_protocol;
  954 +
  955 + /**
  956 + * Return code
  957 + * @var string
  958 + */
  959 + var $_code;
  960 +
  961 + /**
  962 + * Response headers
  963 + * @var array
  964 + */
  965 + var $_headers;
  966 +
  967 + /**
  968 + * Cookies set in response
  969 + * @var array
  970 + */
  971 + var $_cookies;
  972 +
  973 + /**
  974 + * Response body
  975 + * @var string
  976 + */
  977 + var $_body = '';
  978 +
  979 + /**
  980 + * Used by _readChunked(): remaining length of the current chunk
  981 + * @var string
  982 + */
  983 + var $_chunkLength = 0;
  984 +
  985 + /**
  986 + * Attached listeners
  987 + * @var array
  988 + */
  989 + var $_listeners = array();
  990 +
  991 + /**
  992 + * Constructor
  993 + *
  994 + * @param object Net_Socket socket to read the response from
  995 + * @param array listeners attached to request
  996 + * @return mixed PEAR Error on error, true otherwise
  997 + */
  998 + function HTTP_Response(&$sock, &$listeners)
  999 + {
  1000 + $this->_sock =& $sock;
  1001 + $this->_listeners =& $listeners;
  1002 + }
  1003 +
  1004 +
  1005 + /**
  1006 + * Processes a HTTP response
  1007 + *
  1008 + * This extracts response code, headers, cookies and decodes body if it
  1009 + * was encoded in some way
  1010 + *
  1011 + * @access public
  1012 + * @param bool Whether to store response body in object property, set
  1013 + * this to false if downloading a LARGE file and using a Listener.
  1014 + * This is assumed to be true if body is gzip-encoded.
  1015 + * @throws PEAR_Error
  1016 + * @return mixed true on success, PEAR_Error in case of malformed response
  1017 + */
  1018 + function process($saveBody = true)
  1019 + {
  1020 + do {
  1021 + $line = $this->_sock->readLine();
  1022 + if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) {
  1023 + return PEAR::raiseError('Malformed response.');
  1024 + } else {
  1025 + $this->_protocol = 'HTTP/' . $http_version;
  1026 + $this->_code = intval($returncode);
  1027 + }
  1028 + while ('' !== ($header = $this->_sock->readLine())) {
  1029 + $this->_processHeader($header);
  1030 + }
  1031 + } while (100 == $this->_code);
  1032 +
  1033 + $this->_notify('gotHeaders', $this->_headers);
  1034 +
  1035 + // If response body is present, read it and decode
  1036 + $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
  1037 + $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
  1038 + $hasBody = false;
  1039 + if (!isset($this->_headers['content-length']) || 0 != $this->_headers['content-length']) {
  1040 + while (!$this->_sock->eof()) {
  1041 + if ($chunked) {
  1042 + $data = $this->_readChunked();
  1043 + } else {
  1044 + $data = $this->_sock->read(4096);
  1045 + }
  1046 + if ('' == $data) {
  1047 + break;
  1048 + } else {
  1049 + $hasBody = true;
  1050 + if ($saveBody || $gzipped) {
  1051 + $this->_body .= $data;
  1052 + }
  1053 + $this->_notify($gzipped? 'gzTick': 'tick', $data);
  1054 + }
  1055 + }
  1056 + }
  1057 + if ($hasBody) {
  1058 + // Uncompress the body if needed
  1059 + if ($gzipped) {
  1060 + $this->_body = gzinflate(substr($this->_body, 10));
  1061 + $this->_notify('gotBody', $this->_body);
  1062 + } else {
  1063 + $this->_notify('gotBody');
  1064 + }
  1065 + }
  1066 + return true;
  1067 + }
  1068 +
  1069 +
  1070 + /**
  1071 + * Processes the response header
  1072 + *
  1073 + * @access private
  1074 + * @param string HTTP header
  1075 + */
  1076 + function _processHeader($header)
  1077 + {
  1078 + list($headername, $headervalue) = explode(':', $header, 2);
  1079 + $headername = strtolower($headername);
  1080 + $headervalue = ltrim($headervalue);
  1081 +
  1082 + if ('set-cookie' != $headername) {
  1083 + if (isset($this->_headers[$headername])) {
  1084 + $this->_headers[$headername] .= ',' . $headervalue;
  1085 + } else {
  1086 + $this->_headers[$headername] = $headervalue;
  1087 + }
  1088 + } else {
  1089 + $this->_parseCookie($headervalue);
  1090 + }
  1091 + }
  1092 +
  1093 +
  1094 + /**
  1095 + * Parse a Set-Cookie header to fill $_cookies array
  1096 + *
  1097 + * @access private
  1098 + * @param string value of Set-Cookie header
  1099 + */
  1100 + function _parseCookie($headervalue)
  1101 + {
  1102 + $cookie = array(
  1103 + 'expires' => null,
  1104 + 'domain' => null,
  1105 + 'path' => null,
  1106 + 'secure' => false
  1107 + );
  1108 +
  1109 + // Only a name=value pair
  1110 + if (!strpos($headervalue, ';')) {
  1111 + $pos = strpos($headervalue, '=');
  1112 + $cookie['name'] = trim(substr($headervalue, 0, $pos));
  1113 + $cookie['value'] = trim(substr($headervalue, $pos + 1));
  1114 +
  1115 + // Some optional parameters are supplied
  1116 + } else {
  1117 + $elements = explode(';', $headervalue);
  1118 + $pos = strpos($elements[0], '=');
  1119 + $cookie['name'] = trim(substr($elements[0], 0, $pos));
  1120 + $cookie['value'] = trim(substr($elements[0], $pos + 1));
  1121 +
  1122 + for ($i = 1; $i < count($elements); $i++) {
  1123 + if (false === strpos($elements[$i], '=')) {
  1124 + $elName = trim($elements[$i]);
  1125 + $elValue = null;
  1126 + } else {
  1127 + list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
  1128 + }
  1129 + $elName = strtolower($elName);
  1130 + if ('secure' == $elName) {
  1131 + $cookie['secure'] = true;
  1132 + } elseif ('expires' == $elName) {
  1133 + $cookie['expires'] = str_replace('"', '', $elValue);
  1134 + } elseif ('path' == $elName || 'domain' == $elName) {
  1135 + $cookie[$elName] = urldecode($elValue);
  1136 + } else {
  1137 + $cookie[$elName] = $elValue;
  1138 + }
  1139 + }
  1140 + }
  1141 + $this->_cookies[] = $cookie;
  1142 + }
  1143 +
  1144 +
  1145 + /**
  1146 + * Read a part of response body encoded with chunked Transfer-Encoding
  1147 + *
  1148 + * @access private
  1149 + * @return string
  1150 + */
  1151 + function _readChunked()
  1152 + {
  1153 + // at start of the next chunk?
  1154 + if (0 == $this->_chunkLength) {
  1155 + $line = $this->_sock->readLine();
  1156 + if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
  1157 + $this->_chunkLength = hexdec($matches[1]);
  1158 + // Chunk with zero length indicates the end
  1159 + if (0 == $this->_chunkLength) {
  1160 + $this->_sock->readLine(); // make this an eof()
  1161 + return '';
  1162 + }
  1163 + } else {
  1164 + return '';
  1165 + }
  1166 + }
  1167 + $data = $this->_sock->read($this->_chunkLength);
  1168 + $this->_chunkLength -= strlen($data);
  1169 + if (0 == $this->_chunkLength) {
  1170 + $this->_sock->readLine(); // Trailing CRLF
  1171 + }
  1172 + return $data;
  1173 + }
  1174 +
  1175 +
  1176 + /**
  1177 + * Notifies all registered listeners of an event.
  1178 + *
  1179 + * @param string Event name
  1180 + * @param mixed Additional data
  1181 + * @access private
  1182 + * @see HTTP_Request::_notify()
  1183 + */
  1184 + function _notify($event, $data = null)
  1185 + {
  1186 + foreach (array_keys($this->_listeners) as $id) {
  1187 + $this->_listeners[$id]->update($this, $event, $data);
  1188 + }
  1189 + }
  1190 +} // End class HTTP_Response
  1191 +?>
... ...
thirdparty/pear/WebDAV/Request/Listener.php 0 โ†’ 100644
  1 +<?php
  2 +// +-----------------------------------------------------------------------+
  3 +// | Copyright (c) 2002-2003, Richard Heyes |
  4 +// | All rights reserved. |
  5 +// | |
  6 +// | Redistribution and use in source and binary forms, with or without |
  7 +// | modification, are permitted provided that the following conditions |
  8 +// | are met: |
  9 +// | |
  10 +// | o Redistributions of source code must retain the above copyright |
  11 +// | notice, this list of conditions and the following disclaimer. |
  12 +// | o Redistributions in binary form must reproduce the above copyright |
  13 +// | notice, this list of conditions and the following disclaimer in the |
  14 +// | documentation and/or other materials provided with the distribution.|
  15 +// | o The names of the authors may not be used to endorse or promote |
  16 +// | products derived from this software without specific prior written |
  17 +// | permission. |
  18 +// | |
  19 +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  20 +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  21 +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22 +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
  23 +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24 +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
  25 +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26 +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27 +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
  28 +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29 +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
  30 +// | |
  31 +// +-----------------------------------------------------------------------+
  32 +// | Author: Alexey Borzov <avb@php.net> |
  33 +// +-----------------------------------------------------------------------+
  34 +//
  35 +// $Id: Listener.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  36 +//
  37 +
  38 +/**
  39 + * This class implements the Observer part of a Subject-Observer
  40 + * design pattern. It listens to the events sent by a
  41 + * HTTP_Request or HTTP_Response instance.
  42 + *
  43 + * @package HTTP_Request
  44 + * @author Alexey Borzov <avb@php.net>
  45 + * @version $Revision: 6819 $
  46 + */
  47 +class HTTP_Request_Listener
  48 +{
  49 + /**
  50 + * A listener's identifier
  51 + * @var string
  52 + */
  53 + var $_id;
  54 +
  55 + /**
  56 + * Constructor, sets the object's identifier
  57 + *
  58 + * @access public
  59 + */
  60 + function HTTP_Request_Listener()
  61 + {
  62 + $this->_id = md5(uniqid('http_request_', 1));
  63 + }
  64 +
  65 +
  66 + /**
  67 + * Returns the listener's identifier
  68 + *
  69 + * @access public
  70 + * @return string
  71 + */
  72 + function getId()
  73 + {
  74 + return $this->_id;
  75 + }
  76 +
  77 +
  78 + /**
  79 + * This method is called when Listener is notified of an event
  80 + *
  81 + * @access public
  82 + * @param object an object the listener is attached to
  83 + * @param string Event name
  84 + * @param mixed Additional data
  85 + * @abstract
  86 + */
  87 + function update(&$subject, $event, $data = null)
  88 + {
  89 + echo "Notified of event: '$event'\n";
  90 + if (null !== $data) {
  91 + echo "Additional data: ";
  92 + var_dump($data);
  93 + }
  94 + }
  95 +}
  96 +?>
... ...
thirdparty/pear/WebDAV/Upload.php 0 โ†’ 100644
  1 +<?php
  2 +// **********************************************
  3 +//
  4 +// This software is licensed by the LGPL
  5 +// -> http://www.gnu.org/copyleft/lesser.txt
  6 +// (c) 2001-2004 by Tomas Von Veschler Cox
  7 +//
  8 +// **********************************************
  9 +//
  10 +// $Id: Upload.php 6819 2007-06-20 13:09:21Z kevin_fourie $
  11 +
  12 +/*
  13 + * Pear File Uploader class. Easy and secure managment of files
  14 + * submitted via HTML Forms.
  15 + *
  16 + * Leyend:
  17 + * - you can add error msgs in your language in the HTTP_Upload_Error class
  18 + *
  19 + * TODO:
  20 + * - try to think a way of having all the Error system in other
  21 + * file and only include it when an error ocurrs
  22 + *
  23 + * -- Notes for users HTTP_Upload >= 0.9.0 --
  24 + *
  25 + * Error detection was enhanced, so you no longer need to
  26 + * check for PEAR::isError() in $upload->getFiles() or call
  27 + * $upload->isMissing(). Instead you'll
  28 + * get the error when do a check for $file->isError().
  29 + *
  30 + * Example:
  31 + *
  32 + * $upload = new HTTP_Upload('en');
  33 + * $file = $upload->getFiles('i_dont_exist_in_form_definition');
  34 + * if ($file->isError()) {
  35 + * die($file->getMessage());
  36 + * }
  37 + *
  38 + * --
  39 + *
  40 + */
  41 +
  42 +require_once 'PEAR.php';
  43 +
  44 +/**
  45 + * defines default chmod
  46 + */
  47 +define('HTTP_UPLOAD_DEFAULT_CHMOD', 0660);
  48 +
  49 +/**
  50 + * Error Class for HTTP_Upload
  51 + *
  52 + * @author Tomas V.V.Cox <cox@idecnet.com>
  53 + * @see http://vulcanonet.com/soft/index.php?pack=uploader
  54 + * @package HTTP_Upload
  55 + * @category HTTP
  56 + * @access public
  57 + */
  58 +class HTTP_Upload_Error extends PEAR
  59 +{
  60 + /**
  61 + * Selected language for error messages
  62 + * @var string
  63 + */
  64 + var $lang = 'en';
  65 +
  66 + /**
  67 + * Whether HTML entities shall be encoded automatically
  68 + * @var boolean
  69 + */
  70 + var $html = false;
  71 +
  72 + /**
  73 + * Constructor
  74 + *
  75 + * Creates a new PEAR_Error
  76 + *
  77 + * @param string $lang The language selected for error code messages
  78 + * @access public
  79 + */
  80 + function HTTP_Upload_Error($lang = null, $html = false)
  81 + {
  82 + $this->lang = ($lang !== null) ? $lang : $this->lang;
  83 + $this->html = ($html !== false) ? $html : $this->html;
  84 + $ini_size = preg_replace('/m/i', '000000', ini_get('upload_max_filesize'));
  85 +
  86 + if (function_exists('version_compare') &&
  87 + version_compare(phpversion(), '4.1', 'ge')) {
  88 + $maxsize = (isset($_POST['MAX_FILE_SIZE'])) ?
  89 + $_POST['MAX_FILE_SIZE'] : null;
  90 + } else {
  91 + global $HTTP_POST_VARS;
  92 + $maxsize = (isset($HTTP_POST_VARS['MAX_FILE_SIZE'])) ?
  93 + $HTTP_POST_VARS['MAX_FILE_SIZE'] : null;
  94 + }
  95 +
  96 + if (empty($maxsize) || ($maxsize > $ini_size)) {
  97 + $maxsize = $ini_size;
  98 + }
  99 + // XXXXX Add here error messages in your language
  100 + $this->error_codes = array(
  101 + 'TOO_LARGE' => array(
  102 + 'es' => "Fichero demasiado largo. El maximo permitido es: $maxsize bytes.",
  103 + 'en' => "File size too large. The maximum permitted size is: $maxsize bytes.",
  104 + 'de' => "Datei zu gro&szlig;. Die zul&auml;ssige Maximalgr&ouml;&szlig;e ist: $maxsize Bytes.",
  105 + 'nl' => "Het bestand is te groot, de maximale grootte is: $maxsize bytes.",
  106 + 'fr' => "Le fichier est trop gros. La taille maximum autoris&eacute;e est: $maxsize bytes.",
  107 + 'it' => "Il file &eacute; troppo grande. Il massimo permesso &eacute: $maxsize bytes.",
  108 + 'pt_BR' => "Arquivo muito grande. O tamanho m&aacute;ximo permitido &eacute; $maxsize bytes."
  109 + ),
  110 + 'MISSING_DIR' => array(
  111 + 'es' => 'Falta directorio destino.',
  112 + 'en' => 'Missing destination directory.',
  113 + 'de' => 'Kein Zielverzeichnis definiert.',
  114 + 'nl' => 'Geen bestemmings directory.',
  115 + 'fr' => 'Le r&eacute;pertoire de destination n\'est pas d&eacute;fini.',
  116 + 'it' => 'Manca la directory di destinazione.',
  117 + 'pt_BR' => 'Aus&ecirc;ncia de diret&oacute;rio de destino.'
  118 + ),
  119 + 'IS_NOT_DIR' => array(
  120 + 'es' => 'El directorio destino no existe o es un fichero regular.',
  121 + 'en' => 'The destination directory doesn\'t exist or is a regular file.',
  122 + 'de' => 'Das angebene Zielverzeichnis existiert nicht oder ist eine Datei.',
  123 + 'nl' => 'De doeldirectory bestaat niet, of is een gewoon bestand.',
  124 + 'fr' => 'Le r&eacute;pertoire de destination n\'existe pas ou il s\'agit d\'un fichier r&eacute;gulier.',
  125 + 'it' => 'La directory di destinazione non esiste o &eacute; un file.',
  126 + 'pt_BR' => 'O diret&oacute;rio de destino n&atilde;o existe ou &eacute; um arquivo.'
  127 + ),
  128 + 'NO_WRITE_PERMS' => array(
  129 + 'es' => 'El directorio destino no tiene permisos de escritura.',
  130 + 'en' => 'The destination directory doesn\'t have write perms.',
  131 + 'de' => 'Fehlende Schreibrechte f&uuml;r das Zielverzeichnis.',
  132 + 'nl' => 'Geen toestemming om te schrijven in de doeldirectory.',
  133 + 'fr' => 'Le r&eacute;pertoire de destination n\'a pas les droits en &eacute;criture.',
  134 + 'it' => 'Non si hanno i permessi di scrittura sulla directory di destinazione.',
  135 + 'pt_BR' => 'O diret&oacute;rio de destino n&atilde;o possui permiss&atilde;o para escrita.'
  136 + ),
  137 + 'NO_USER_FILE' => array(
  138 + 'es' => 'No se ha escogido fichero para el upload.',
  139 + 'en' => 'You haven\'t selected any file for uploading.',
  140 + 'de' => 'Es wurde keine Datei f&uuml;r den Upload ausgew&auml;hlt.',
  141 + 'nl' => 'Er is geen bestand opgegeven om te uploaden.',
  142 + 'fr' => 'Vous n\'avez pas s&eacute;lectionn&eacute; de fichier &agrave; envoyer.',
  143 + 'it' => 'Nessun file selezionato per l\'upload.',
  144 + 'pt_BR' => 'Nenhum arquivo selecionado para upload.'
  145 + ),
  146 + 'BAD_FORM' => array(
  147 + 'es' => 'El formulario no contiene method="post" enctype="multipart/form-data" requerido.',
  148 + 'en' => 'The html form doesn\'t contain the required method="post" enctype="multipart/form-data".',
  149 + 'de' => 'Das HTML-Formular enth&auml;lt nicht die Angabe method="post" enctype="multipart/form-data" '.
  150 + 'im &gt;form&lt;-Tag.',
  151 + 'nl' => 'Het HTML-formulier bevat niet de volgende benodigde '.
  152 + 'eigenschappen: method="post" enctype="multipart/form-data".',
  153 + 'fr' => 'Le formulaire HTML ne contient pas les attributs requis : '.
  154 + ' method="post" enctype="multipart/form-data".',
  155 + 'it' => 'Il modulo HTML non contiene gli attributi richiesti: "'.
  156 + ' method="post" enctype="multipart/form-data".',
  157 + 'pt_BR' => 'O formul&aacute;rio HTML n&atilde;o possui o method="post" enctype="multipart/form-data" requerido.'
  158 + ),
  159 + 'E_FAIL_COPY' => array(
  160 + 'es' => 'Fallo al copiar el fichero temporal.',
  161 + 'en' => 'Failed to copy the temporary file.',
  162 + 'de' => 'Tempor&auml;re Datei konnte nicht kopiert werden.',
  163 + 'nl' => 'Het tijdelijke bestand kon niet gekopieerd worden.',
  164 + 'fr' => 'L\'enregistrement du fichier temporaire a &eacute;chou&eacute;.',
  165 + 'it' => 'Copia del file temporaneo fallita.',
  166 + 'pt_BR' => 'Falha ao copiar o arquivo tempor&aacute;rio.'
  167 + ),
  168 + 'E_FAIL_MOVE' => array(
  169 + 'es' => 'No puedo mover el fichero.',
  170 + 'en' => 'Impossible to move the file.',
  171 + 'de' => 'Datei kann nicht verschoben werden.',
  172 + 'nl' => 'Het bestand kon niet verplaatst worden.',
  173 + 'fr' => 'Impossible de d&eacute;placer le fichier.',
  174 + 'pt_BR' => 'N&atilde;o foi poss&iacute;vel mover o arquivo.'
  175 + ),
  176 + 'FILE_EXISTS' => array(
  177 + 'es' => 'El fichero destino ya existe.',
  178 + 'en' => 'The destination file already exists.',
  179 + 'de' => 'Die zu erzeugende Datei existiert bereits.',
  180 + 'nl' => 'Het doelbestand bestaat al.',
  181 + 'fr' => 'Le fichier de destination existe d&eacute;j&agrave;.',
  182 + 'it' => 'File destinazione gi&agrave; esistente.',
  183 + 'pt_BR' => 'O arquivo de destino j&aacute; existe.'
  184 + ),
  185 + 'CANNOT_OVERWRITE' => array(
  186 + 'es' => 'El fichero destino ya existe y no se puede sobreescribir.',
  187 + 'en' => 'The destination file already exists and could not be overwritten.',
  188 + 'de' => 'Die zu erzeugende Datei existiert bereits und konnte nicht &uuml;berschrieben werden.',
  189 + 'nl' => 'Het doelbestand bestaat al, en kon niet worden overschreven.',
  190 + 'fr' => 'Le fichier de destination existe d&eacute;j&agrave; et ne peux pas &ecirc;tre remplac&eacute;.',
  191 + 'it' => 'File destinazione gi&agrave; esistente e non si pu&ograve; sovrascrivere.',
  192 + 'pt_BR' => 'O arquivo de destino j&aacute; existe e n&atilde;o p&ocirc;de ser sobrescrito.'
  193 + ),
  194 + 'NOT_ALLOWED_EXTENSION' => array(
  195 + 'es' => 'Extension de fichero no permitida.',
  196 + 'en' => 'File extension not permitted.',
  197 + 'de' => 'Unerlaubte Dateiendung.',
  198 + 'nl' => 'Niet toegestane bestands-extensie.',
  199 + 'fr' => 'Le fichier a une extension non autoris&eacute;e.',
  200 + 'it' => 'Estensione del File non permessa.',
  201 + 'pt_BR' => 'Extens&atilde;o de arquivo n&atilde;o permitida.'
  202 + ),
  203 + 'PARTIAL' => array(
  204 + 'es' => 'El fichero fue parcialmente subido',
  205 + 'en' => 'The file was only partially uploaded.',
  206 + 'de' => 'Die Datei wurde unvollst&auml;ndig &uuml;bertragen.',
  207 + 'nl' => 'Het bestand is slechts gedeeltelijk geupload.',
  208 + 'pt_BR' => 'O arquivo nรฃo foi enviado por completo.'
  209 + ),
  210 + 'ERROR' => array(
  211 + 'es' => 'Error en subida:',
  212 + 'en' => 'Upload error:',
  213 + 'de' => 'Fehler beim Upload:',
  214 + 'nl' => 'Upload fout:',
  215 + 'pt_BR' => 'Erro de upload:'
  216 + ),
  217 + 'DEV_NO_DEF_FILE' => array(
  218 + 'es' => 'No estรก definido en el formulario este nombre de fichero como &lt;input type="file" name=?&gt;.',
  219 + 'en' => 'This filename is not defined in the form as &lt;input type="file" name=?&gt;.',
  220 + 'de' => 'Dieser Dateiname ist im Formular nicht als &lt;input type="file" name=?&gt; definiert.',
  221 + 'nl' => 'Deze bestandsnaam is niett gedefineerd in het formulier als &lt;input type="file" name=?&gt;.'
  222 + )
  223 + );
  224 + }
  225 +
  226 + /**
  227 + * returns the error code
  228 + *
  229 + * @param string $e_code type of error
  230 + * @return string Error message
  231 + */
  232 + function errorCode($e_code)
  233 + {
  234 + if (!empty($this->error_codes[$e_code][$this->lang])) {
  235 + $msg = $this->html ?
  236 + html_entity_decode($this->error_codes[$e_code][$this->lang]) :
  237 + $this->error_codes[$e_code][$this->lang];
  238 + } else {
  239 + $msg = $e_code;
  240 + }
  241 +
  242 + if (!empty($this->error_codes['ERROR'][$this->lang])) {
  243 + $error = $this->error_codes['ERROR'][$this->lang];
  244 + } else {
  245 + $error = $this->error_codes['ERROR']['en'];
  246 + }
  247 + return $error.' '.$msg;
  248 + }
  249 +
  250 + /**
  251 + * Overwrites the PEAR::raiseError method
  252 + *
  253 + * @param string $e_code type of error
  254 + * @return object PEAR_Error a PEAR-Error object
  255 + * @access public
  256 + */
  257 + function raiseError($e_code)
  258 + {
  259 + return PEAR::raiseError($this->errorCode($e_code), $e_code);
  260 + }
  261 +}
  262 +
  263 +/**
  264 + * This class provides an advanced file uploader system
  265 + * for file uploads made from html forms
  266 +
  267 + *
  268 + * @author Tomas V.V.Cox <cox@idecnet.com>
  269 + * @see http://vulcanonet.com/soft/index.php?pack=uploader
  270 + * @package HTTP_Upload
  271 + * @category HTTP
  272 + * @access public
  273 + */
  274 +class HTTP_Upload extends HTTP_Upload_Error
  275 +{
  276 + /**
  277 + * Contains an array of "uploaded files" objects
  278 + * @var array
  279 + */
  280 + var $files = array();
  281 +
  282 + /**
  283 + * Contains the desired chmod for uploaded files
  284 + * @var int
  285 + * @access private
  286 + */
  287 + var $_chmod = HTTP_UPLOAD_DEFAULT_CHMOD;
  288 +
  289 + /**
  290 + * Constructor
  291 + *
  292 + * @param string $lang Language to use for reporting errors
  293 + * @see Upload_Error::error_codes
  294 + * @access public
  295 + */
  296 + function HTTP_Upload($lang = null)
  297 + {
  298 + $this->HTTP_Upload_Error($lang);
  299 + if (function_exists('version_compare') &&
  300 + version_compare(phpversion(), '4.1', 'ge'))
  301 + {
  302 + $this->post_files = $_FILES;
  303 + if (isset($_SERVER['CONTENT_TYPE'])) {
  304 + $this->content_type = $_SERVER['CONTENT_TYPE'];
  305 + }
  306 + } else {
  307 + global $HTTP_POST_FILES, $HTTP_SERVER_VARS;
  308 + $this->post_files = $HTTP_POST_FILES;
  309 + if (isset($HTTP_SERVER_VARS['CONTENT_TYPE'])) {
  310 + $this->content_type = $HTTP_SERVER_VARS['CONTENT_TYPE'];
  311 + }
  312 + }
  313 + }
  314 +
  315 + /**
  316 + * Get files
  317 + *
  318 + * @param mixed $file If:
  319 + * - not given, function will return array of upload_file objects
  320 + * - is int, will return the $file position in upload_file objects array
  321 + * - is string, will return the upload_file object corresponding
  322 + * to $file name of the form. For ex:
  323 + * if form is <input type="file" name="userfile">
  324 + * to get this file use: $upload->getFiles('userfile')
  325 + *
  326 + * @return mixed array or object (see @param $file above) or Pear_Error
  327 + * @access public
  328 + */
  329 + function &getFiles($file = null)
  330 + {
  331 + static $is_built = false;
  332 + //build only once for multiple calls
  333 + if (!$is_built) {
  334 + $files = &$this->_buildFiles();
  335 + if (PEAR::isError($files)) {
  336 + // there was an error with the form.
  337 + // Create a faked upload embedding the error
  338 + $this->files['_error'] = &new HTTP_Upload_File(
  339 + '_error', null,
  340 + null, null,
  341 + null, $files->getCode(),
  342 + $this->lang, $this->_chmod);
  343 + } else {
  344 + $this->files = $files;
  345 + }
  346 + $is_built = true;
  347 + }
  348 + if ($file !== null) {
  349 + if (is_int($file)) {
  350 + $pos = 0;
  351 + foreach ($this->files as $obj) {
  352 + if ($pos == $file) {
  353 + return $obj;
  354 + }
  355 + $pos++;
  356 + }
  357 + } elseif (is_string($file) && isset($this->files[$file])) {
  358 + return $this->files[$file];
  359 + }
  360 + if (isset($this->files['_error'])) {
  361 + return $this->files['_error'];
  362 + } else {
  363 + // developer didn't specify this name in the form
  364 + // warn him about it with a faked upload
  365 + return new HTTP_Upload_File(
  366 + '_error', null,
  367 + null, null,
  368 + null, 'DEV_NO_DEF_FILE',
  369 + $this->lang);
  370 + }
  371 + }
  372 + return $this->files;
  373 + }
  374 +
  375 + /**
  376 + * Creates the list of the uploaded file
  377 + *
  378 + * @return array of HTTP_Upload_File objects for every file
  379 + */
  380 + function &_buildFiles()
  381 + {
  382 + // Form method check
  383 + if (!isset($this->content_type) ||
  384 + strpos($this->content_type, 'multipart/form-data') !== 0)
  385 + {
  386 + return $this->raiseError('BAD_FORM');
  387 + }
  388 + // In 4.1 $_FILES isn't initialized when no uploads
  389 + // XXX (cox) afaik, in >= 4.1 and <= 4.3 only
  390 + if (function_exists('version_compare') &&
  391 + version_compare(phpversion(), '4.1', 'ge'))
  392 + {
  393 + $error = $this->isMissing();
  394 + if (PEAR::isError($error)) {
  395 + return $error;
  396 + }
  397 + }
  398 +
  399 + // map error codes from 4.2.0 $_FILES['userfile']['error']
  400 + if (function_exists('version_compare') &&
  401 + version_compare(phpversion(), '4.2.0', 'ge')) {
  402 + $uploadError = array(
  403 + 1 => 'TOO_LARGE',
  404 + 2 => 'TOO_LARGE',
  405 + 3 => 'PARTIAL',
  406 + 4 => 'NO_USER_FILE'
  407 + );
  408 + }
  409 +
  410 +
  411 + // Parse $_FILES (or $HTTP_POST_FILES)
  412 + $files = array();
  413 + foreach ($this->post_files as $userfile => $value) {
  414 + if (is_array($value['name'])) {
  415 + foreach ($value['name'] as $key => $val) {
  416 + $err = $value['error'][$key];
  417 + if (isset($err) && $err !== 0 && isset($uploadError[$err])) {
  418 + $error = $uploadError[$err];
  419 + } else {
  420 + $error = null;
  421 + }
  422 + $name = basename($value['name'][$key]);
  423 + $tmp_name = $value['tmp_name'][$key];
  424 + $size = $value['size'][$key];
  425 + $type = $value['type'][$key];
  426 + $formname = $userfile . "[$key]";
  427 + $files[$formname] = new HTTP_Upload_File($name, $tmp_name,
  428 + $formname, $type, $size, $error, $this->lang, $this->_chmod);
  429 + }
  430 + // One file
  431 + } else {
  432 + $err = $value['error'];
  433 + if (isset($err) && $err !== 0 && isset($uploadError[$err])) {
  434 + $error = $uploadError[$err];
  435 + } else {
  436 + $error = null;
  437 + }
  438 + $name = basename($value['name']);
  439 + $tmp_name = $value['tmp_name'];
  440 + $size = $value['size'];
  441 + $type = $value['type'];
  442 + $formname = $userfile;
  443 + $files[$formname] = new HTTP_Upload_File($name, $tmp_name,
  444 + $formname, $type, $size, $error, $this->lang, $this->_chmod);
  445 + }
  446 + }
  447 + return $files;
  448 + }
  449 +
  450 + /**
  451 + * Checks if the user submited or not some file
  452 + *
  453 + * @return mixed False when are files or PEAR_Error when no files
  454 + * @access public
  455 + * @see Read the note in the source code about this function
  456 + */
  457 + function isMissing()
  458 + {
  459 + if (count($this->post_files) < 1) {
  460 + return $this->raiseError('NO_USER_FILE');
  461 + }
  462 + //we also check if at least one file has more than 0 bytes :)
  463 + $files = array();
  464 + $size = 0;
  465 + foreach ($this->post_files as $userfile => $value) {
  466 + if (is_array($value['name'])) {
  467 + foreach ($value['name'] as $key => $val) {
  468 + $size += $value['size'][$key];
  469 + }
  470 + } else { //one file
  471 + $size = $value['size'];
  472 + }
  473 + }
  474 + if ($size == 0) {
  475 + $this->raiseError('NO_USER_FILE');
  476 + }
  477 + return false;
  478 + }
  479 +
  480 + /**
  481 + * Sets the chmod to be used for uploaded files
  482 + *
  483 + * @param int Desired mode
  484 + */
  485 + function setChmod($mode)
  486 + {
  487 + $this->_chmod = $mode;
  488 + }
  489 +}
  490 +
  491 +/**
  492 + * This class provides functions to work with the uploaded file
  493 + *
  494 + * @author Tomas V.V.Cox <cox@idecnet.com>
  495 + * @see http://vulcanonet.com/soft/index.php?pack=uploader
  496 + * @package HTTP_Upload
  497 + * @category HTTP
  498 + * @access public
  499 + */
  500 +class HTTP_Upload_File extends HTTP_Upload_Error
  501 +{
  502 + /**
  503 + * If the random seed was initialized before or not
  504 + * @var boolean;
  505 + */
  506 + var $_seeded = 0;
  507 +
  508 + /**
  509 + * Assoc array with file properties
  510 + * @var array
  511 + */
  512 + var $upload = array();
  513 +
  514 + /**
  515 + * If user haven't selected a mode, by default 'safe' will be used
  516 + * @var boolean
  517 + */
  518 + var $mode_name_selected = false;
  519 +
  520 + /**
  521 + * It's a common security risk in pages who has the upload dir
  522 + * under the document root (remember the hack of the Apache web?)
  523 + *
  524 + * @var array
  525 + * @access private
  526 + * @see HTTP_Upload_File::setValidExtensions()
  527 + */
  528 + var $_extensions_check = array('php', 'phtm', 'phtml', 'php3', 'inc');
  529 +
  530 + /**
  531 + * @see HTTP_Upload_File::setValidExtensions()
  532 + * @var string
  533 + * @access private
  534 + */
  535 + var $_extensions_mode = 'deny';
  536 +
  537 + /**
  538 + * Contains the desired chmod for uploaded files
  539 + * @var int
  540 + * @access private
  541 + */
  542 + var $_chmod = HTTP_UPLOAD_DEFAULT_CHMOD;
  543 +
  544 + /**
  545 + * Constructor
  546 + *
  547 + * @param string $name destination file name
  548 + * @param string $tmp temp file name
  549 + * @param string $formname name of the form
  550 + * @param string $type Mime type of the file
  551 + * @param string $size size of the file
  552 + * @param string $error error on upload
  553 + * @param string $lang used language for errormessages
  554 + * @access public
  555 + */
  556 + function HTTP_Upload_File($name = null, $tmp = null, $formname = null,
  557 + $type = null, $size = null, $error = null,
  558 + $lang = null, $chmod = HTTP_UPLOAD_DEFAULT_CHMOD)
  559 + {
  560 + $this->HTTP_Upload_Error($lang);
  561 + $ext = null;
  562 +
  563 + if (empty($name) || $size == 0) {
  564 + $error = 'NO_USER_FILE';
  565 + } elseif ($tmp == 'none') {
  566 + $error = 'TOO_LARGE';
  567 + } else {
  568 + // strpos needed to detect files without extension
  569 + if (($pos = strrpos($name, '.')) !== false) {
  570 + $ext = substr($name, $pos + 1);
  571 + }
  572 + }
  573 +
  574 + if (function_exists('version_compare') &&
  575 + version_compare(phpversion(), '4.1', 'ge')) {
  576 + if (isset($_POST['MAX_FILE_SIZE']) &&
  577 + $size > $_POST['MAX_FILE_SIZE']) {
  578 + $error = 'TOO_LARGE';
  579 + }
  580 + } else {
  581 + global $HTTP_POST_VARS;
  582 + if (isset($HTTP_POST_VARS['MAX_FILE_SIZE']) &&
  583 + $size > $HTTP_POST_VARS['MAX_FILE_SIZE']) {
  584 + $error = 'TOO_LARGE';
  585 + }
  586 + }
  587 +
  588 + $this->upload = array(
  589 + 'real' => $name,
  590 + 'name' => $name,
  591 + 'form_name' => $formname,
  592 + 'ext' => $ext,
  593 + 'tmp_name' => $tmp,
  594 + 'size' => $size,
  595 + 'type' => $type,
  596 + 'error' => $error
  597 + );
  598 +
  599 + $this->_chmod = $chmod;
  600 + }
  601 +
  602 + /**
  603 + * Sets the name of the destination file
  604 + *
  605 + * @param string $mode A valid mode: 'uniq', 'safe' or 'real' or a file name
  606 + * @param string $prepend A string to prepend to the name
  607 + * @param string $append A string to append to the name
  608 + *
  609 + * @return string The modified name of the destination file
  610 + * @access public
  611 + */
  612 + function setName($mode, $prepend = null, $append = null)
  613 + {
  614 + switch ($mode) {
  615 + case 'uniq':
  616 + $name = $this->nameToUniq();
  617 + $this->upload['ext'] = $this->nameToSafe($this->upload['ext'], 10);
  618 + $name .= '.' . $this->upload['ext'];
  619 + break;
  620 + case 'safe':
  621 + $name = $this->nameToSafe($this->upload['real']);
  622 + if (($pos = strrpos($name, '.')) !== false) {
  623 + $this->upload['ext'] = substr($name, $pos + 1);
  624 + } else {
  625 + $this->upload['ext'] = '';
  626 + }
  627 + break;
  628 + case 'real':
  629 + $name = $this->upload['real'];
  630 + break;
  631 + default:
  632 + $name = $mode;
  633 + }
  634 + $this->upload['name'] = $prepend . $name . $append;
  635 + $this->mode_name_selected = true;
  636 + return $this->upload['name'];
  637 + }
  638 +
  639 + /**
  640 + * Unique file names in the form: 9022210413b75410c28bef.html
  641 + * @see HTTP_Upload_File::setName()
  642 + */
  643 + function nameToUniq()
  644 + {
  645 + if (! $this->_seeded) {
  646 + srand((double) microtime() * 1000000);
  647 + $this->_seeded = 1;
  648 + }
  649 + $uniq = uniqid(rand());
  650 + return $uniq;
  651 + }
  652 +
  653 + /**
  654 + * Format a file name to be safe
  655 + *
  656 + * @param string $file The string file name
  657 + * @param int $maxlen Maximun permited string lenght
  658 + * @return string Formatted file name
  659 + * @see HTTP_Upload_File::setName()
  660 + */
  661 + function nameToSafe($name, $maxlen=250)
  662 + {
  663 + $noalpha = 'รร‰รร“รšรรกรฉรญรณรบรฝร‚รŠรŽร”ร›รขรชรฎรดรปร€รˆรŒร’ร™ร รจรฌรฒรนร„ร‹รร–รœรครซรฏรถรผรฟรƒรฃร•รตร…รฅร‘รฑร‡รง@ยฐยบยช';
  664 + $alpha = 'AEIOUYaeiouyAEIOUaeiouAEIOUaeiouAEIOUaeiouyAaOoAaNnCcaooa';
  665 +
  666 + $name = substr($name, 0, $maxlen);
  667 + $name = strtr($name, $noalpha, $alpha);
  668 + // not permitted chars are replaced with "_"
  669 + return preg_replace('/[^a-zA-Z0-9,._\+\()\-]/', '_', $name);
  670 + }
  671 +
  672 + /**
  673 + * The upload was valid
  674 + *
  675 + * @return bool If the file was submitted correctly
  676 + * @access public
  677 + */
  678 + function isValid()
  679 + {
  680 + if ($this->upload['error'] === null) {
  681 + return true;
  682 + }
  683 + return false;
  684 + }
  685 +
  686 + /**
  687 + * User haven't submit a file
  688 + *
  689 + * @return bool If the user submitted a file or not
  690 + * @access public
  691 + */
  692 + function isMissing()
  693 + {
  694 + if ($this->upload['error'] == 'NO_USER_FILE') {
  695 + return true;
  696 + }
  697 + return false;
  698 + }
  699 +
  700 + /**
  701 + * Some error occured during upload (most common due a file size problem,
  702 + * like max size exceeded or 0 bytes long).
  703 + * @return bool If there were errors submitting the file (probably
  704 + * because the file excess the max permitted file size)
  705 + * @access public
  706 + */
  707 + function isError()
  708 + {
  709 + if (in_array($this->upload['error'], array('TOO_LARGE', 'BAD_FORM','DEV_NO_DEF_FILE'))) {
  710 + return true;
  711 + }
  712 + return false;
  713 + }
  714 +
  715 + /**
  716 + * Moves the uploaded file to its destination directory.
  717 + *
  718 + * @param string $dir_dest Destination directory
  719 + * @param bool $overwrite Overwrite if destination file exists?
  720 + * @return mixed True on success or Pear_Error object on error
  721 + * @access public
  722 + */
  723 + function moveTo($dir_dest, $overwrite = true)
  724 + {
  725 + if (!$this->isValid()) {
  726 + return $this->raiseError($this->upload['error']);
  727 + }
  728 +
  729 + //Valid extensions check
  730 + if (!$this->_evalValidExtensions()) {
  731 + return $this->raiseError('NOT_ALLOWED_EXTENSION');
  732 + }
  733 +
  734 + $err_code = $this->_chk_dir_dest($dir_dest);
  735 + if ($err_code !== false) {
  736 + return $this->raiseError($err_code);
  737 + }
  738 + // Use 'safe' mode by default if no other was selected
  739 + if (!$this->mode_name_selected) {
  740 + $this->setName('safe');
  741 + }
  742 +
  743 + $name_dest = $dir_dest . DIRECTORY_SEPARATOR . $this->upload['name'];
  744 +
  745 + if (@is_file($name_dest)) {
  746 + if ($overwrite !== true) {
  747 + return $this->raiseError('FILE_EXISTS');
  748 + } elseif (!is_writable($name_dest)) {
  749 + return $this->raiseError('CANNOT_OVERWRITE');
  750 + }
  751 + }
  752 +
  753 + // copy the file and let php clean the tmp
  754 + if (!@move_uploaded_file($this->upload['tmp_name'], $name_dest)) {
  755 + return $this->raiseError('E_FAIL_MOVE');
  756 + }
  757 + @chmod($name_dest, $this->_chmod);
  758 + return $this->getProp('name');
  759 + }
  760 +
  761 + /**
  762 + * Check for a valid destination dir
  763 + *
  764 + * @param string $dir_dest Destination dir
  765 + * @return mixed False on no errors or error code on error
  766 + */
  767 + function _chk_dir_dest($dir_dest)
  768 + {
  769 + if (!$dir_dest) {
  770 + return 'MISSING_DIR';
  771 + }
  772 + if (!@is_dir ($dir_dest)) {
  773 + return 'IS_NOT_DIR';
  774 + }
  775 + if (!is_writeable ($dir_dest)) {
  776 + return 'NO_WRITE_PERMS';
  777 + }
  778 + return false;
  779 + }
  780 + /**
  781 + * Retrive properties of the uploaded file
  782 + * @param string $name The property name. When null an assoc array with
  783 + * all the properties will be returned
  784 + * @return mixed A string or array
  785 + * @see HTTP_Upload_File::HTTP_Upload_File()
  786 + * @access public
  787 + */
  788 + function getProp($name = null)
  789 + {
  790 + if ($name === null) {
  791 + return $this->upload;
  792 + }
  793 + return $this->upload[$name];
  794 + }
  795 +
  796 + /**
  797 + * Returns a error message, if a error occured
  798 + * (deprecated) Use getMessage() instead
  799 + * @return string a Error message
  800 + * @access public
  801 + */
  802 + function errorMsg()
  803 + {
  804 + return $this->errorCode($this->upload['error']);
  805 + }
  806 +
  807 + /**
  808 + * Returns a error message, if a error occured
  809 + * @return string a Error message
  810 + * @access public
  811 + */
  812 + function getMessage()
  813 + {
  814 + return $this->errorCode($this->upload['error']);
  815 + }
  816 +
  817 + /**
  818 + * Function to restrict the valid extensions on file uploads
  819 + *
  820 + * @param array $exts File extensions to validate
  821 + * @param string $mode The type of validation:
  822 + * 1) 'deny' Will deny only the supplied extensions
  823 + * 2) 'accept' Will accept only the supplied extensions
  824 + * as valid
  825 + * @access public
  826 + */
  827 + function setValidExtensions($exts, $mode = 'deny')
  828 + {
  829 + $this->_extensions_check = $exts;
  830 + $this->_extensions_mode = $mode;
  831 + }
  832 +
  833 + /**
  834 + * Evaluates the validity of the extensions set by setValidExtensions
  835 + *
  836 + * @return bool False on non valid extension, true if they are valid
  837 + * @access private
  838 + */
  839 + function _evalValidExtensions()
  840 + {
  841 + $exts = $this->_extensions_check;
  842 + settype($exts, 'array');
  843 + if ($this->_extensions_mode == 'deny') {
  844 + if (in_array($this->getProp('ext'), $exts)) {
  845 + return false;
  846 + }
  847 + // mode == 'accept'
  848 + } else {
  849 + if (!in_array($this->getProp('ext'), $exts)) {
  850 + return false;
  851 + }
  852 + }
  853 + return true;
  854 + }
  855 +}
  856 +?>
0 857 \ No newline at end of file
... ...