HTTP_WebDAV_Client
[ class tree: HTTP_WebDAV_Client ] [ index: HTTP_WebDAV_Client ] [ all elements ]

Source for file Stream.php

Documentation is available at Stream.php

  1. <?php
  2. require_once "HTTP/Request.php";
  3.  
  4. require "HTTP/WebDAV/Tools/_parse_propfind_response.php";
  5. require "HTTP/WebDAV/Tools/_parse_lock_response.php";
  6.  
  7. // WebDAV defines some addition HTTP methods
  8. define('HTTP_REQUEST_METHOD_COPY',      'COPY',      true);
  9. define('HTTP_REQUEST_METHOD_MOVE',      'MOVE',      true);
  10. define('HTTP_REQUEST_METHOD_MKCOL',     'MKCOL',     true);
  11. define('HTTP_REQUEST_METHOD_PROPFIND',  'PROPFIND',  true);
  12. define('HTTP_REQUEST_METHOD_PROPPATCH''PROPPATCH'true);
  13. define('HTTP_REQUEST_METHOD_LOCK',      'LOCK',      true);
  14. define('HTTP_REQUEST_METHOD_UNLOCK',    'UNLOCK',    true);
  15.  
  16. /**
  17.  * A stream wrapper class for WebDAV access
  18.  *
  19.  * @access public
  20.  */
  21. class HTTP_WebDAV_Client_Stream 
  22. {
  23.      /**
  24.      * User-Agent: header string
  25.      *
  26.      * @access private
  27.      * @var    string 
  28.      */
  29.     var $userAgent "PEAR::HTTP_WebDAV_Client";
  30.  
  31.     /**
  32.      * Content-type: header string
  33.      *
  34.      * @access private
  35.      * @var    string 
  36.      */
  37.     var $contentType "application/octet-stream";
  38.  
  39.     /**
  40.      * The http or https resource URL
  41.      *
  42.      * @access private
  43.      * @var    string  url
  44.      */
  45.     var $url = false;
  46.  
  47.     /**
  48.      * The resource URL path
  49.      *
  50.      * @access private
  51.      * @var    string  path
  52.      */
  53.     var $path = false;
  54.  
  55.     /**
  56.      * File position indicator
  57.      *
  58.      * @access private
  59.      * @var    int     offset in bytes
  60.      */
  61.     var $position = 0;
  62.  
  63.     /**
  64.      * File status information cache
  65.      *
  66.      * @access private
  67.      * @var    array   stat information
  68.      */
  69.     var $stat = array();
  70.  
  71.     /**
  72.      * User name for authentication
  73.      *
  74.      * @access private
  75.      * @var    string  name
  76.      */
  77.     var $user = false;
  78.  
  79.     /**
  80.      * Password for authentication
  81.      *
  82.      * @access private
  83.      * @var    string  password
  84.      */
  85.     var $pass = false;
  86.  
  87.     /**
  88.      * WebDAV protocol levels supported by the server
  89.      *
  90.      * @access private
  91.      * @var    array   level entries
  92.      */
  93.     var $dav_level = array();
  94.  
  95.     /**
  96.      * HTTP methods supported by the server
  97.      *
  98.      * @access private
  99.      * @var    array   method entries
  100.      */
  101.     var $dav_allow = array();
  102.  
  103.     /**
  104.      * Directory content cache
  105.      *
  106.      * @access private
  107.      * @var    array   filename entries
  108.      */
  109.     var $dirfiles = false;
  110.  
  111.     /**
  112.      * Current readdir() position
  113.      *
  114.      * @access private
  115.      * @var    int 
  116.      */
  117.     var $dirpos = 0;
  118.  
  119.     /**
  120.      * Remember if end of file was reached
  121.      *
  122.      * @access private
  123.      * @var    bool 
  124.      */
  125.     var $eof = false;
  126.  
  127.     /**
  128.      * Lock token
  129.      *
  130.      * @access private
  131.      * @var    string 
  132.      */
  133.     var $locktoken = false;
  134.  
  135.     /**
  136.      * Stream wrapper interface open() method
  137.      *
  138.      * @access public
  139.      * @var    string resource URL
  140.      * @var    string mode flags
  141.      * @var    array  not used here
  142.      * @var    string return real path here if suitable
  143.      * @return bool   true on success
  144.      */
  145.     function stream_open($path$mode$options&$opened_path
  146.     {
  147.         // rewrite the request URL
  148.         if (!$this->_parse_url($path)) return false;
  149.  
  150.         // query server for WebDAV options
  151.         if (!$this->_check_options())  return false;
  152.  
  153.         // now get the file metadata
  154.         // we only need type, size, creation and modification date
  155.         $req &$this->_startRequest(HTTP_REQUEST_METHOD_PROPFIND);
  156.         if (is_string($this->user)) {
  157.             $req->setBasicAuth($this->user@$this->pass);          
  158.         }
  159.         $req->addHeader("Depth""0");
  160.         $req->addHeader("Content-Type""text/xml");
  161.         $req->addRawPostData('<?xml version="1.0" encoding="utf-8"?>
  162. <propfind xmlns="DAV:">
  163.  <prop>
  164.   <resourcetype/>
  165.   <getcontentlength/>
  166.   <getlastmodified />
  167.   <creationdate/>
  168.  </prop>
  169. </propfind>
  170. ');
  171.         $req->sendRequest();
  172.  
  173.         // check the response code, anything but 207 indicates a problem
  174.         switch ($req->getResponseCode()) {
  175.         case 207: // OK
  176.             // now we have to parse the result to get the status info items
  177.             $propinfo &new HTTP_WebDAV_Client_parse_propfind_response($req->getResponseBody());
  178.             $this->stat $propinfo->stat();
  179.             unset($propinfo);
  180.             break;
  181.  
  182.         case 404: // not found is ok in write modes
  183.             if (preg_match('|[aw\+]|'$mode)) {
  184.                 break; // write
  185.             
  186.             $this->eof = true;
  187.             // else fallthru
  188.         default: 
  189.             error_log("file not found: ".$req->getResponseCode());
  190.             return false;
  191.         }
  192.         
  193.         // 'w' -> open for writing, truncate existing files
  194.         if (strpos($mode"w"!== false{
  195.             $req &$this->_startRequest(HTTP_REQUEST_METHOD_PUT);
  196.  
  197.             $req->addHeader('Content-length'0);
  198.  
  199.             if (is_string($this->user)) {
  200.                 $req->setBasicAuth($this->user@$this->pass);          
  201.             }
  202.  
  203.             $req->sendRequest();
  204.         }
  205.  
  206.         // 'a' -> open for appending
  207.         if (strpos($mode"a"!== false{
  208.             $this->position $this->stat['size'];            
  209.             $this->eof = true;
  210.         }
  211.  
  212.         // we are done :)
  213.         return true;
  214.     }
  215.  
  216.  
  217.     /**
  218.      * Streap wrapper interface close() method
  219.      *
  220.      * @access public
  221.      */
  222.     function stream_close(
  223.     {
  224.         // unlock?
  225.         if ($this->locktoken{
  226.             $this->stream_lock(LOCK_UN);
  227.         }
  228.  
  229.         // closing is simple as HTTP is stateless 
  230.         $this->url = false;
  231.     }
  232.  
  233.     /**
  234.      * Stream wrapper interface stat() method
  235.      *
  236.      * @access public
  237.      * @return array  stat entries
  238.      */
  239.     function stream_stat(
  240.     {
  241.         // we already have collected the needed information 
  242.         // in stream_open() :)
  243.         return $this->stat;
  244.     }
  245.  
  246.     /**
  247.      * Stream wrapper interface read() method
  248.      *
  249.      * @access public
  250.      * @param  int    requested byte count
  251.      * @return string read data
  252.      */
  253.     function stream_read($count
  254.     {
  255.         // do some math
  256.         $start $this->position;
  257.         $end   $start $count - 1;
  258.  
  259.         // create a GET request with a range
  260.         $req &$this->_startRequest(HTTP_REQUEST_METHOD_GET);
  261.         if (is_string($this->user)) {
  262.             $req->setBasicAuth($this->user@$this->pass);          
  263.         }
  264.         $req->addHeader("Range""bytes=$start-$end");
  265.  
  266.         // go! go! go!
  267.         $req->sendRequest();
  268.         $data $req->getResponseBody();
  269.         $len  strlen($data);
  270.  
  271.         // lets see what happened
  272.         switch ($req->getResponseCode()) {
  273.         case 200: 
  274.             // server doesn't support range requests 
  275.             // TODO we should add some sort of cacheing here
  276.             $data substr($data$start$count);
  277.             break;
  278.  
  279.         case 206:
  280.             // server supports range requests
  281.             break;
  282.  
  283.         case 416:
  284.             // reading beyond end of file is not an error
  285.             $data "";
  286.             $len  = 0;
  287.             break;
  288.  
  289.         default: 
  290.             return false;
  291.         }
  292.  
  293.         // no data indicates end of file
  294.         if (!$len{
  295.             $this->eof = true;
  296.         }
  297.  
  298.         // update position
  299.         $this->position += $len;
  300.  
  301.         // thats it!
  302.         return $data;
  303.     }
  304.  
  305.     /**
  306.      * Stream wrapper interface write() method
  307.      *
  308.      * @access public
  309.      * @param  string data to write
  310.      * @return int    number of bytes actually written
  311.      */
  312.     function stream_write($buffer
  313.     {
  314.         // do some math
  315.         $start $this->position;
  316.         $end   $this->position strlen($buffer- 1;
  317.  
  318.         // create a partial PUT request
  319.         $req &$this->_startRequest(HTTP_REQUEST_METHOD_PUT);
  320.         if (is_string($this->user)) {
  321.             $req->setBasicAuth($this->user@$this->pass);          
  322.         }
  323.         $req->addHeader("Content-Range""bytes $start-$end/*");
  324.         if ($this->locktoken{
  325.             $req->addHeader("If""(<{$this->locktoken}>)");
  326.         }
  327.         $req->addRawPostData($buffer);
  328.  
  329.         // go! go! go!
  330.         $req->sendRequest();
  331.  
  332.         // check result
  333.         switch ($req->getResponseCode()) {
  334.         case 200:
  335.         case 201:
  336.         case 204:
  337.             $this->position += strlen($buffer);
  338.             return 1 + $end $start;
  339.             
  340.         default: 
  341.             return false;
  342.         }
  343.  
  344.         /* 
  345.          We do not cope with servers that do not support partial PUTs!
  346.          And we do assume that a server does conform to the following 
  347.          rule from RFC 2616 Section 9.6:
  348.          "The recipient of the entity MUST NOT ignore any Content-* 
  349.          (e.g. Content-Range) headers that it does not understand or 
  350.          implement and MUST return a 501 (Not Implemented) response 
  351.          in such cases."
  352.            
  353.          So the worst case scenario with a compliant server not 
  354.          implementing partial PUTs should be a failed request. A 
  355.          server simply ignoring "Content-Range" would replace 
  356.          file contents with the request body instead of putting
  357.          the data at the requested place but we can blame it 
  358.          for not being compliant in this case ;)
  359.          (TODO: maybe we should do a HTTP version check first?)
  360.  
  361.          we *could* emulate partial PUT support by adding local
  362.          cacheing but for now we don't want to as it adds a lot
  363.          of complexity and storage overhead to the client ...
  364.         */
  365.     }
  366.  
  367.     /**
  368.      * Stream wrapper interface eof() method
  369.      *
  370.      * @access public
  371.      * @return bool   true if end of file was reached
  372.      */
  373.     function stream_eof() 
  374.     {
  375.         // another simple one 
  376.         return $this->eof;
  377.     }
  378.  
  379.     /**
  380.      * Stream wrapper interface tell() method
  381.      *
  382.      * @access public
  383.      * @return int    current file position
  384.      */
  385.     function stream_tell() 
  386.     {
  387.         // just return the current position
  388.         return $this->position;
  389.     }
  390.  
  391.     /**
  392.      * Stream wrapper interface seek() method
  393.      *
  394.      * @access public
  395.      * @param  int    position to seek to
  396.      * @param  int    seek mode
  397.      * @return bool   true on success
  398.      */
  399.     function stream_seek($pos, $whence) 
  400.     {
  401.         switch ($whence) {
  402.         case SEEK_SET:
  403.             // absolute position
  404.             $this->position = $pos;
  405.             break;
  406.         case SEEK_CUR:
  407.             // relative position
  408.             $this->position += $pos;
  409.             break;
  410.         case SEEK_END:
  411.             // relative position form end
  412.             $this->position = $this->stat['size'] + $pos;
  413.             break;
  414.         default: 
  415.             return false;
  416.         }
  417.  
  418.         // TODO: this is rather naive (check how libc handles this)
  419.         $this->eof = false;
  420.  
  421.         return true;
  422.     }
  423.  
  424.  
  425. /**    
  426.      * Stream wrapper interface URL stat() method
  427.      *
  428.      * @access public
  429.      * @param  string URL to get stat information for
  430.      * @return array  stat information
  431.      */
  432.     function url_stat($url) 
  433.     {
  434.         // we map this one to open()/stat()/close()
  435.         // there won't be much gain in inlining this
  436.         if (!$this->stream_open($url, "r", array(), $dummy)) {
  437.             return false;
  438.         }
  439.         $stat =  $this->stream_stat();
  440.         $this->stream_close();
  441.  
  442.         return $stat;
  443.     }
  444.  
  445.  
  446.  
  447.  
  448.  
  449. /**    
  450.      * Stream wrapper interface opendir() method
  451.      *
  452.      * @access public
  453.      * @param  string directory resource URL
  454.      * @param  array  not used here
  455.      * @return bool   true on success
  456.      */
  457.     function dir_opendir($path, $options) 
  458.     {
  459.         // rewrite the request URL
  460.         if (!$this->_parse_url($path)) return false;
  461.  
  462.         // query server for WebDAV options
  463.         if (!$this->_check_options())  return false;
  464.  
  465.         if (!isset($this->dav_allow[<a href="../HTTP_WebDAV_Client/_HTTP_WebDAV_Client-1.0.2---HTTP---WebDAV---Client---Stream.php.html#defineHTTP_REQUEST_METHOD_PROPFIND">HTTP_REQUEST_METHOD_PROPFIND</a>])) {
  466.             return false;
  467.         }
  468.  
  469.         // now read the directory
  470.         $req = &$this->_startRequest(<a href="../HTTP_WebDAV_Client/_HTTP_WebDAV_Client-1.0.2---HTTP---WebDAV---Client---Stream.php.html#defineHTTP_REQUEST_METHOD_PROPFIND">HTTP_REQUEST_METHOD_PROPFIND</a>);
  471.         if (is_string($this->user)) {
  472.             $req->setBasicAuth($this->user, @$this->pass);          
  473.         }
  474.         $req->addHeader("Depth", "1");
  475.         $req->addHeader("Content-Type", "text/xml");
  476.         $req->addRawPostData('<?xml version="1.0" encoding="utf-8"?>
  477. <propfind xmlns="DAV:">
  478.  <prop>
  479.   <resourcetype/>
  480.   <getcontentlength/>
  481.   <creationdate/>
  482.   <getlastmodified/>
  483.  </prop>
  484. </propfind>
  485. ');
  486.         $req->sendRequest();
  487.  
  488.         switch ($req->getResponseCode()) {
  489.         case 207: // multistatus content
  490.             $this->dirfiles = array();
  491.             $this->dirpos = 0;
  492.  
  493.             // for all returned resource entries
  494.             foreach (explode("\n", $req->getResponseBody()) as $line) {
  495.                 // Preg_match_all if the whole response is one line!
  496.                 if (preg_match_all("/href>([^<]*)/", $line, $matches)) {
  497.                     // skip the directory itself                    
  498.                     foreach ($matches[1] as $match){
  499.                         // Compare to $this->url too
  500.                         if ($match == "" || $match == $this->path || $match == $this->url) {
  501.                             continue;
  502.                         }
  503.                         // just remember the basenames to return them later with readdir()
  504.                         $this->dirfiles[] = basename($match);
  505.                     }
  506.                 }
  507.             }
  508.             return true;
  509.  
  510.         default: 
  511.             // any other response state indicates an error
  512.             error_log("file not found");
  513.             return false;
  514.         }
  515.     }
  516.  
  517.  
  518. /**    
  519.      * Stream wrapper interface readdir() method
  520.      *
  521.      * @access public
  522.      * @return string filename
  523.      */
  524.     function dir_readdir() 
  525.     {
  526.         // bailout if directory is empty
  527.         if (!is_array($this->dirfiles)) {
  528.             return false;
  529.         }
  530.         
  531.         // bailout if we already reached end of dir
  532.         if ($this->dirpos >= count($this->dirfiles)) {
  533.             return false;
  534.         }
  535.  
  536.         // return an entry and move on
  537.         return $this->dirfiles[$this->dirpos++];
  538.     }
  539.  
  540. /**    
  541.      * Stream wrapper interface rewinddir() method
  542.      *
  543.      * @access public
  544.      */
  545.     function dir_rewinddir() 
  546.     {
  547.         // bailout if directory content info has already
  548.         // been freed
  549.         if (!is_array($this->dirfiles)) {
  550.             return false;
  551.         }
  552.  
  553.         // rewind to first entry
  554.         $this->dirpos = 0;
  555.     }
  556.  
  557. /**    
  558.      * Stream wrapper interface closedir() method
  559.      *
  560.      * @access public
  561.      */
  562.     function dir_closedir() 
  563.     {
  564.         // free stored directory content
  565.         if (is_array($this->dirfiles)) {
  566.             $this->dirfiles = false;
  567.             $this->dirpos = 0;
  568.         }
  569.     }
  570.  
  571.  
  572. /**    
  573.      * Stream wrapper interface mkdir() method
  574.      *
  575.      * @access public
  576.      * @param  string collection URL to be created
  577.      * @return bool   true on access
  578.      */
  579.     function mkdir($path) 
  580.     {
  581.         // rewrite the request URL
  582.         if (!$this->_parse_url($path)) return false;
  583.  
  584.         // query server for WebDAV options
  585.         if (!$this->_check_options())  return false;
  586.  
  587.         $req = &$this->_startRequest(<a href="../HTTP_WebDAV_Client/_HTTP_WebDAV_Client-1.0.2---HTTP---WebDAV---Client---Stream.php.html#defineHTTP_REQUEST_METHOD_MKCOL">HTTP_REQUEST_METHOD_MKCOL</a>);
  588.         if (is_string($this->user)) {
  589.             $req->setBasicAuth($this->user, @$this->pass);          
  590.         }
  591.         if ($this->locktoken) {
  592.             $req->addHeader("If", "(<{$this->locktoken}>)");
  593.         }
  594.         $req->sendRequest();
  595.  
  596.         // check the response code, anything but 201 indicates a problem
  597.         $stat = $req->getResponseCode();
  598.         switch ($stat) {
  599.         case 201:
  600.             return true;
  601.         default:
  602.             error_log("mkdir failed - "$stat);
  603.             return false;
  604.         }
  605.     }
  606.  
  607.  
  608. /**    
  609.      * Stream wrapper interface rmdir() method
  610.      *
  611.      * @access public
  612.      * @param  string collection URL to be created
  613.      * @return bool   true on access
  614.      */
  615.     function rmdir($path) 
  616.     {
  617.         // TODO: this should behave like "rmdir", currently it is more like "rm -rf"
  618.         // rewrite the request URL
  619.         if (!$this->_parse_url($path)) return false;
  620.  
  621.         // query server for WebDAV options
  622.         if (!$this->_check_options())  return false;
  623.  
  624.         $req = &$this->_startRequest(HTTP_REQUEST_METHOD_DELETE);
  625.         if (is_string($this->user)) {
  626.             $req->setBasicAuth($this->user, @$this->pass);          
  627.         }
  628.         if ($this->locktoken) {
  629.             $req->addHeader("If", "(<{$this->locktoken}>)");
  630.         }
  631.         $req->sendRequest();
  632.  
  633.         // check the response code, anything but 204 indicates a problem
  634.         $stat = $req->getResponseCode();
  635.         switch ($stat) {
  636.         case 204:
  637.             return true;
  638.         default:
  639.             error_log("rmdir failed - "$stat);
  640.             return false;
  641.         }
  642.     }
  643.      
  644.  
  645. /**    
  646.      * Stream wrapper interface rename() method
  647.      *
  648.      * @access public
  649.      * @param  string resource URL to be moved
  650.      * @param  string resource URL to move to
  651.      * @return bool   true on access
  652.      */
  653.     function rename($path, $new_path) 
  654.     {
  655.         // rewrite the request URL
  656.         if (!$this->_parse_url($path)) return false;
  657.  
  658.         // query server for WebDAV options
  659.         if (!$this->_check_options())  return false;
  660.  
  661.         $req = &$this->_startRequest(<a href="../HTTP_WebDAV_Client/_HTTP_WebDAV_Client-1.0.2---HTTP---WebDAV---Client---Stream.php.html#defineHTTP_REQUEST_METHOD_MOVE">HTTP_REQUEST_METHOD_MOVE</a>);
  662.         if (is_string($this->user)) {
  663.             $req->setBasicAuth($this->user, @$this->pass);          
  664.         }
  665.         if ($this->locktoken) {
  666.             $req->addHeader("If", "(<{$this->locktoken}>)");
  667.         }
  668.         if (!$this->_parse_url($new_path)) return false;
  669.         $req->addHeader("Destination", $this->url);
  670.         $req->sendRequest();
  671.  
  672.         // check the response code, anything but 207 indicates a problem
  673.         $stat = $req->getResponseCode();
  674.         switch ($stat) {
  675.         case 201:
  676.         case 204:
  677.             return true;
  678.         default:
  679.             error_log("rename failed - "$stat);
  680.             return false;
  681.         }
  682.     }
  683.      
  684.  
  685.     /**
  686.      * Stream wrapper interface unlink() method
  687.      *
  688.      * @access public
  689.      * @param  string resource URL to be removed
  690.      * @return bool   true on success
  691.      */
  692.     function unlink($path) 
  693.     {
  694.         // rewrite the request URL
  695.         if (!$this->_parse_url($path)) return false;
  696.  
  697.         // query server for WebDAV options
  698.         if (!$this->_check_options())  return false;
  699.  
  700.         // is DELETE supported?
  701.         if (!isset($this->dav_allow[HTTP_REQUEST_METHOD_DELETE])) {
  702.             return false;
  703.         }       
  704.  
  705.         $req = &$this->_startRequest(HTTP_REQUEST_METHOD_DELETE);
  706.         if (is_string($this->user)) {
  707.             $req->setBasicAuth($this->user, @$this->pass);          
  708.         }
  709.         if ($this->locktoken) {
  710.             $req->addHeader("If", "(<{$this->locktoken}>)");
  711.         }
  712.         $req->sendRequest();
  713.  
  714.         switch ($req->getResponseCode()) {
  715.         case 204: // ok
  716.             return true;
  717.         default: 
  718.             return false;
  719.         }
  720.     }
  721.         
  722.  
  723.     /**
  724.      * Static helper that registers the wrappers
  725.      *
  726.      * @access public, static
  727.      * @return bool   true on success (even if SSL doesn't work)
  728.      */
  729.     function register() 
  730.     {
  731.         // check that we have the required feature
  732.         if (!function_exists("stream_register_wrapper")) {
  733.             return false;
  734.         }
  735.  
  736.         // try to register the non-encrypted WebDAV wrapper
  737.         if (!stream_register_wrapper("webdav", "HTTP_WebDAV_Client_Stream")) {
  738.             return false;
  739.         }
  740.  
  741.         // now try to register the SSL protocol variant
  742.         // it is not critical if this fails
  743.         // TODO check whether SSL is possible with HTTP_Request
  744.         stream_register_wrapper("webdavs", "HTTP_WebDAV_Client_Stream");
  745.  
  746.         return true;
  747.     }
  748.  
  749.  
  750.     /**
  751.      * Helper function for URL analysis
  752.      *
  753.      * @access private
  754.      * @param  string  original request URL
  755.      * @return bool    true on success else false
  756.      */
  757.     function _parse_url($path) 
  758.     {
  759.         // rewrite the WebDAV url as a plain HTTP url
  760.         $url = parse_url($path);
  761.  
  762.         // detect whether plain or SSL-encrypted transfer is requested
  763.         $scheme = $url['scheme'];
  764.         switch ($scheme) {
  765.         case "webdav":
  766.             $url['scheme'] = "http";
  767.             break;
  768.         case "webdavs":
  769.             $url['scheme'] = "https";
  770.             break;
  771.         default:
  772.             error_log("only 'webdav:' and 'webdavs:' are supported, not '$url[scheme]:'");
  773.             return false;
  774.         }
  775.  
  776.         if (isset($this->context)) {
  777.             // extract settings from stream context
  778.             $context = stream_context_get_options($this->context);
  779.  
  780.             // User-Agent
  781.             if (isset($context[$scheme]['user_agent'])) {
  782.                 $this->userAgent = $context[$scheme]['user_agent'];
  783.             }
  784.  
  785.             // Content-Type
  786.             if (isset($context[$scheme]['content_type'])) {
  787.                 $this->contentType = $context[$scheme]['content_type'];
  788.             }
  789.             
  790.             // TODO check whether to implement other HTTP specific
  791.             // context settings from http://php.net/manual/en/context.http.php
  792.         }
  793.  
  794.  
  795.         // if a TCP port is specified we have to add it after the host
  796.         if (isset($url['port'])) {
  797.             $url['host'] .= ":$url[port]";
  798.         }
  799.  
  800.         // store the plain path for possible later use
  801.         $this->path = $url["path"];
  802.  
  803.         // now we can put together the new URL
  804.         $this->url = "$url[scheme]://$url[host]$url[path]";
  805.  
  806.         // extract authentication information
  807.         if (isset($url['user'])) {
  808.             $this->user = urldecode($url['user']);
  809.         }
  810.         if (isset($url['pass'])) {
  811.             $this->pass = urldecode($url['pass']);
  812.         }
  813.  
  814.         return true;
  815.     }
  816.  
  817.     /**
  818.      * Helper function for WebDAV OPTIONS detection
  819.      *
  820.      * @access private
  821.      * @return bool    true on success else false
  822.      */
  823.     function _check_options() 
  824.     {
  825.         // now check OPTIONS reply for WebDAV response headers
  826.         $req = &$this->_startRequest(HTTP_REQUEST_METHOD_OPTIONS);
  827.         if (is_string($this->user)) {
  828.             $req->setBasicAuth($this->user, @$this->pass);          
  829.         }
  830.         $req->sendRequest();
  831.         if ($req->getResponseCode() != 200) {
  832.             return false;
  833.         }
  834.  
  835.         // get the supported DAV levels and extensions
  836.         $dav = $req->getResponseHeader("DAV");
  837.         $this->dav_level = array();
  838.         foreach (explode(",", $dav) as $level) {
  839.             $this->dav_level[trim($level)] = true;
  840.         }
  841.         if (!isset($this->dav_level["1"])) {
  842.             // we need at least DAV Level 1 conformance
  843.             return false;
  844.         }
  845.         
  846.         // get the supported HTTP methods
  847.         // TODO these are not checked for WebDAV compliance yet
  848.         $allow = $req->getResponseHeader("Allow");
  849.         $this->dav_allow = array();
  850.         foreach (explode(",", $allow) as $method) {
  851.             $this->dav_allow[trim($method)] = true;
  852.         }
  853.  
  854.         // TODO check for required WebDAV methods
  855.         return true;
  856.     }
  857.  
  858.  
  859.     /**
  860.      * Stream handler interface lock() method (experimental ...)
  861.      *
  862.      * @access private
  863.      * @return bool    true on success else false
  864.      */
  865.     function stream_lock($mode) 
  866.     {
  867.         /* TODO:
  868.          - think over how to refresh locks
  869.         */
  870.         
  871.         $ret = false;
  872.  
  873.         // LOCK is only supported by DAV Level 2
  874.         if (!isset($this->dav_level["2"])) {
  875.             return false;
  876.         }
  877.  
  878.         switch ($mode & ~LOCK_NB) {
  879.         case LOCK_UN:
  880.             if ($this->locktoken) {
  881.                 $req = &$this->_startRequest(<a href="../HTTP_WebDAV_Client/_HTTP_WebDAV_Client-1.0.2---HTTP---WebDAV---Client---Stream.php.html#defineHTTP_REQUEST_METHOD_UNLOCK">HTTP_REQUEST_METHOD_UNLOCK</a>);
  882.                 if (is_string($this->user)) {
  883.                     $req->setBasicAuth($this->user, @$this->pass);          
  884.                 }
  885.                 $req->addHeader("Lock-Token", "<{$this->locktoken}>");
  886.                 $req->sendRequest();
  887.  
  888.                 $ret = $req->getResponseCode() == 204;
  889.             }
  890.             break;
  891.  
  892.         case LOCK_SH:
  893.         case LOCK_EX:
  894.             $body = sprintf('<?xml version="1.0" encoding="utf-8" ?> 
  895. <D:lockinfo xmlns:D="DAV:"> 
  896.  <D:lockscope><D:%s/></D:lockscope> 
  897.  <D:locktype><D:write/></D:locktype> 
  898.  <D:owner>%s</D:owner> 
  899. </D:lockinfo>',
  900.                             ($mode & LOCK_SH) ? "shared" : "exclusive",
  901.                             get_class($this))// TODO better owner string
  902.             $req = &$this->_startRequest(<a href="../HTTP_WebDAV_Client/_HTTP_WebDAV_Client-1.0.2---HTTP---WebDAV---Client---Stream.php.html#defineHTTP_REQUEST_METHOD_LOCK">HTTP_REQUEST_METHOD_LOCK</a>);
  903.             if (is_string($this->user)) {
  904.                 $req->setBasicAuth($this->user, @$this->pass);          
  905.             }
  906.             if ($this->locktoken) { // needed for refreshing a lock
  907.                 $req->addHeader("Lock-Token", "<{$this->locktoken}>");
  908.             }
  909.             $req->addHeader("Timeout", "Infinite, Second-4100000000");
  910.             $req->addHeader("Content-Type", 'text/xml; charset="utf-8"');
  911.             $req->addRawPostData($body);
  912.             $req->sendRequest();
  913.  
  914.             $ret = $req->getResponseCode() == 200;          
  915.  
  916.             if ($ret) {
  917.                 $propinfo = &new HTTP_WebDAV_Client_parse_lock_response($req->getResponseBody());               
  918.                 $this->locktoken = $propinfo->locktoken;
  919.                 // TODO deal with timeout
  920.             }
  921.             break;
  922.             
  923.         default:
  924.             break;
  925.         }
  926.  
  927.         return $ret;
  928.     }
  929.  
  930.     function &_startRequest($method)
  931.     {
  932.         $req = &new HTTP_Request($this->url);
  933.  
  934.         $req->addHeader('User-agent',   $this->userAgent);
  935.         $req->addHeader('Content-type', $this->contentType);
  936.  
  937.         $req->setMethod($method);
  938.  
  939.         return $req;        
  940.     }
  941. }
  942.  
  943. /*
  944.  * Local variables:
  945.  * tab-width: 4
  946.  * c-basic-offset: 4
  947.  * indent-tabs-mode:nil
  948.  * End:

Documentation generated on Mon, 11 Mar 2019 15:46:45 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.