Changeset 21237


Ignore:
Timestamp:
2011/09/09 15:29:59 (9 years ago)
Author:
Seasoft
Message:

#1475 (PEAR::HTTP_Request の配置が誤っている)
#1476 (インクルードしているライブラリをバージョンアップする)

  • PEAR::Net_URL
  • PEAR::HTTP_Request
Location:
branches/version-2_11-dev
Files:
3 added
6 edited
1 moved

Legend:

Unmodified
Added
Removed
  • branches/version-2_11-dev/data/class/pages/admin/LC_Page_Admin_Home.php

    r20970 r21237  
    2525// {{{ requires 
    2626require_once CLASS_EX_REALDIR . 'page_extends/admin/LC_Page_Admin_Ex.php'; 
    27 require_once DATA_REALDIR . 'module/Request.php'; 
     27require_once DATA_REALDIR . 'module/HTTP/Request.php'; 
    2828 
    2929/** 
  • branches/version-2_11-dev/data/class/pages/admin/basis/LC_Page_Admin_Basis_ZipInstall.php

    r20970 r21237  
    2424// {{{ requires 
    2525require_once CLASS_EX_REALDIR . 'page_extends/admin/LC_Page_Admin_Ex.php'; 
    26 require_once DATA_REALDIR . 'module/Request.php'; 
     26require_once DATA_REALDIR . 'module/HTTP/Request.php'; 
    2727 
    2828/** CSV ファイルの最大行数 */ 
     
    368368   function lfDownloadZipFileFromJp() { 
    369369        // Proxy経由を可能とする。 
    370         // TODO Proxyの設定は「DATA_REALDIR . 'module/Request.php'」内の「function HTTP_Request」へ記述する。いずれは、外部設定としたい。 
     370        // TODO Proxyの設定は「DATA_REALDIR . 'module/HTTP/Request.php'」内の「function HTTP_Request」へ記述する。いずれは、外部設定としたい。 
    371371        $req = new HTTP_Request(); 
    372372 
  • branches/version-2_11-dev/data/class/pages/upgrade/LC_Page_Upgrade_Base.php

    r20970 r21237  
    44require_once CLASS_REALDIR . 'pages/upgrade/helper/LC_Upgrade_Helper_Log.php'; 
    55require_once CLASS_REALDIR . 'pages/upgrade/helper/LC_Upgrade_Helper_Json.php'; 
    6 require_once DATA_REALDIR . 'module/Request.php'; 
     6require_once DATA_REALDIR . 'module/HTTP/Request.php'; 
    77 
    88/** 
  • branches/version-2_11-dev/data/class/util/SC_Utils.php

    r21204 r21237  
    12861286    // 指定したURLに対してPOSTでデータを送信する 
    12871287    function sfSendPostData($url, $arrData, $arrOkCode = array()){ 
    1288         require_once DATA_REALDIR . 'module/Request.php'; 
     1288        require_once DATA_REALDIR . 'module/HTTP/Request.php'; 
    12891289 
    12901290        // 送信インスタンス生成 
  • branches/version-2_11-dev/data/module/HTTP/Request.php

    r20116 r21237  
    11<?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$ 
    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 if(!defined('REQUEST_PHP_DIR')) { 
    47     $REQUEST_PHP_DIR = realpath(dirname( __FILE__)); 
    48     define("REQUEST_PHP_DIR", $REQUEST_PHP_DIR);     
    49 } 
    50  
    51 require_once REQUEST_PHP_DIR . '/PEAR.php'; 
    52 require_once REQUEST_PHP_DIR . '/Net/Socket.php'; 
    53 require_once REQUEST_PHP_DIR . '/Net/URL.php'; 
    54  
     2/** 
     3 * Class for performing HTTP requests 
     4 * 
     5 * PHP versions 4 and 5 
     6 * 
     7 * LICENSE: 
     8 * 
     9 * Copyright (c) 2002-2007, Richard Heyes 
     10 * All rights reserved. 
     11 * 
     12 * Redistribution and use in source and binary forms, with or without 
     13 * modification, are permitted provided that the following conditions 
     14 * are met: 
     15 * 
     16 * o Redistributions of source code must retain the above copyright 
     17 *   notice, this list of conditions and the following disclaimer. 
     18 * o Redistributions in binary form must reproduce the above copyright 
     19 *   notice, this list of conditions and the following disclaimer in the 
     20 *   documentation and/or other materials provided with the distribution. 
     21 * o The names of the authors may not be used to endorse or promote 
     22 *   products derived from this software without specific prior written 
     23 *   permission. 
     24 * 
     25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
     26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
     27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
     28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
     29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
     30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
     31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
     32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
     33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
     34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     36 * 
     37 * @category    HTTP 
     38 * @package     HTTP_Request 
     39 * @author      Richard Heyes <richard@phpguru.org> 
     40 * @author      Alexey Borzov <avb@php.net> 
     41 * @copyright   2002-2007 Richard Heyes 
     42 * @license     http://opensource.org/licenses/bsd-license.php New BSD License 
     43 * @version     CVS: $Id$ 
     44 * @link        http://pear.php.net/package/HTTP_Request/ 
     45 */ 
     46 
     47/** 
     48 * PEAR and PEAR_Error classes (for error handling) 
     49 */ 
     50require_once 'PEAR.php'; 
     51/** 
     52 * Socket class 
     53 */ 
     54require_once 'Net/Socket.php'; 
     55/** 
     56 * URL handling class 
     57 */ 
     58require_once 'Net/URL.php'; 
     59 
     60/**#@+ 
     61 * Constants for HTTP request methods 
     62 */ 
    5563define('HTTP_REQUEST_METHOD_GET',     'GET',     true); 
    5664define('HTTP_REQUEST_METHOD_HEAD',    'HEAD',    true); 
     
    6068define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true); 
    6169define('HTTP_REQUEST_METHOD_TRACE',   'TRACE',   true); 
    62  
     70/**#@-*/ 
     71 
     72/**#@+ 
     73 * Constants for HTTP request error codes 
     74 */ 
     75define('HTTP_REQUEST_ERROR_FILE',             1); 
     76define('HTTP_REQUEST_ERROR_URL',              2); 
     77define('HTTP_REQUEST_ERROR_PROXY',            4); 
     78define('HTTP_REQUEST_ERROR_REDIRECTS',        8); 
     79define('HTTP_REQUEST_ERROR_RESPONSE',        16); 
     80define('HTTP_REQUEST_ERROR_GZIP_METHOD',     32); 
     81define('HTTP_REQUEST_ERROR_GZIP_READ',       64); 
     82define('HTTP_REQUEST_ERROR_GZIP_DATA',      128); 
     83define('HTTP_REQUEST_ERROR_GZIP_CRC',       256); 
     84/**#@-*/ 
     85 
     86/**#@+ 
     87 * Constants for HTTP protocol versions 
     88 */ 
    6389define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true); 
    6490define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true); 
    65  
    66 class HTTP_Request { 
    67  
     91/**#@-*/ 
     92 
     93if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) { 
     94   /** 
     95    * Whether string functions are overloaded by their mbstring equivalents 
     96    */ 
     97    define('HTTP_REQUEST_MBSTRING', true); 
     98} else { 
     99   /** 
     100    * @ignore 
     101    */ 
     102    define('HTTP_REQUEST_MBSTRING', false); 
     103} 
     104 
     105/** 
     106 * Class for performing HTTP requests 
     107 * 
     108 * Simple example (fetches yahoo.com and displays it): 
     109 * <code> 
     110 * $a = &new HTTP_Request('http://www.yahoo.com/'); 
     111 * $a->sendRequest(); 
     112 * echo $a->getResponseBody(); 
     113 * </code> 
     114 * 
     115 * @category    HTTP 
     116 * @package     HTTP_Request 
     117 * @author      Richard Heyes <richard@phpguru.org> 
     118 * @author      Alexey Borzov <avb@php.net> 
     119 * @version     Release: 1.4.4 
     120 */ 
     121class HTTP_Request 
     122{ 
     123   /**#@+ 
     124    * @access private 
     125    */ 
    68126    /** 
    69127    * Instance of Net_URL 
    70     * @var object Net_URL 
     128    * @var Net_URL 
    71129    */ 
    72130    var $_url; 
     
    95153    */ 
    96154    var $_user; 
    97      
     155 
    98156    /** 
    99157    * Basic Auth Password 
     
    104162    /** 
    105163    * Socket object 
    106     * @var object Net_Socket 
     164    * @var Net_Socket 
    107165    */ 
    108166    var $_sock; 
    109      
     167 
    110168    /** 
    111169    * Proxy server 
     
    113171    */ 
    114172    var $_proxy_host; 
    115      
     173 
    116174    /** 
    117175    * Proxy port 
     
    119177    */ 
    120178    var $_proxy_port; 
    121      
     179 
    122180    /** 
    123181    * Proxy username 
     
    125183    */ 
    126184    var $_proxy_user; 
    127      
     185 
    128186    /** 
    129187    * Proxy password 
     
    139197 
    140198   /** 
    141     * Request body   
     199    * Request body 
    142200    * @var string 
    143201    */ 
     
    151209 
    152210   /** 
    153     * Files to post  
     211    * Methods having defined semantics for request body 
     212    * 
     213    * Content-Length header (indicating that the body follows, section 4.3 of 
     214    * RFC 2616) will be sent for these methods even if no body was added 
     215    * 
     216    * @var array 
     217    */ 
     218    var $_bodyRequired = array('POST', 'PUT'); 
     219 
     220   /** 
     221    * Files to post 
    154222    * @var array 
    155223    */ 
     
    161229    */ 
    162230    var $_timeout; 
    163      
     231 
    164232    /** 
    165233    * HTTP_Response object 
    166     * @var object HTTP_Response 
     234    * @var HTTP_Response 
    167235    */ 
    168236    var $_response; 
    169      
     237 
    170238    /** 
    171239    * Whether to allow redirects 
     
    173241    */ 
    174242    var $_allowRedirects; 
    175      
     243 
    176244    /** 
    177245    * Maximum redirects allowed 
     
    179247    */ 
    180248    var $_maxRedirects; 
    181      
     249 
    182250    /** 
    183251    * Current number of redirects 
     
    199267 
    200268   /** 
    201     * Whether to save response body in response object property   
     269    * Whether to save response body in response object property 
    202270    * @var bool 
    203271    */ 
     
    215283    */ 
    216284    var $_socketOptions = null; 
     285   /**#@-*/ 
    217286 
    218287    /** 
     
    273342 
    274343        // Default useragent 
    275         //$this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )'); 
     344        $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )'); 
    276345 
    277346        // We don't do keep-alives by default 
     
    289358 
    290359        // Use gzip encoding if possible 
    291         // Avoid gzip encoding if using multibyte functions (see #1781) 
    292         if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib') && 
    293             0 == (2 & ini_get('mbstring.func_overload'))) { 
    294  
     360        if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) { 
    295361            $this->addHeader('Accept-Encoding', 'gzip'); 
    296362        } 
    297363    } 
    298      
     364 
    299365    /** 
    300366    * Generates a Host header for HTTP/1.1 requests 
     
    313379        } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) { 
    314380            $host = $this->_url->host . ':' . $this->_url->port; 
    315          
     381 
    316382        } else { 
    317383            $host = $this->_url->host; 
     
    320386        return $host; 
    321387    } 
    322      
     388 
    323389    /** 
    324390    * Resets the object to its initial state (DEPRECATED). 
     
    357423        if (empty($this->_url->path)) { 
    358424            $this->_url->path = '/'; 
    359         }  
    360     } 
    361      
    362    /** 
    363     * Returns the current request URL   
     425        } 
     426    } 
     427 
     428   /** 
     429    * Returns the current request URL 
    364430    * 
    365431    * @return   string  Current request URL 
    366432    * @access   public 
    367433    */ 
    368     function getUrl($url) 
     434    function getUrl() 
    369435    { 
    370436        return empty($this->_url)? '': $this->_url->getUrl(); 
     
    464530    { 
    465531        $this->_url->addQueryString($name, $value, $preencoded); 
    466     }     
    467      
     532    } 
     533 
    468534    /** 
    469535    * Sets the querystring to literally what you supply 
     
    494560        } 
    495561    } 
    496      
    497     function addPostDataArray($array, $preencoded = false) 
    498     { 
    499         foreach($array as $key => $val){ 
    500             $this->addPostData($key, $val, $preencoded); 
    501         } 
    502     }    
    503562 
    504563   /** 
    505564    * Recursively applies the callback function to the value 
    506     *  
     565    * 
    507566    * @param    mixed   Callback function 
    508567    * @param    mixed   Value to process 
     
    524583 
    525584   /** 
    526     * Adds a file to upload 
    527     *  
    528     * This also changes content-type to 'multipart/form-data' for proper upload 
    529     *  
     585    * Adds a file to form-based file upload 
     586    * 
     587    * Used to emulate file upload via a HTML form. The method also sets 
     588    * Content-Type of HTTP request to 'multipart/form-data'. 
     589    * 
     590    * If you just want to send the contents of a file as the body of HTTP 
     591    * request you should use setBody() method. 
     592    * 
    530593    * @access public 
    531594    * @param  string    name of file-upload field 
     
    538601    { 
    539602        if (!is_array($fileName) && !is_readable($fileName)) { 
    540             return PEAR::raiseError("File '{$fileName}' is not readable"); 
     603            return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE); 
    541604        } elseif (is_array($fileName)) { 
    542605            foreach ($fileName as $name) { 
    543606                if (!is_readable($name)) { 
    544                     return PEAR::raiseError("File '{$name}' is not readable"); 
     607                    return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE); 
    545608                } 
    546609            } 
     
    579642 
    580643    /** 
    581     * Clears any postdata that has been added (DEPRECATED).  
    582     *  
     644    * Clears any postdata that has been added (DEPRECATED). 
     645    * 
    583646    * Useful for multiple request scenarios. 
    584647    * 
     
    593656    /** 
    594657    * Appends a cookie to "Cookie:" header 
    595     *  
     658    * 
    596659    * @param string $name cookie name 
    597660    * @param string $value cookie value 
     
    603666        $this->addHeader('Cookie', $cookies . $name . '=' . $value); 
    604667    } 
    605      
    606     /** 
    607     * Clears any cookies that have been added (DEPRECATED).  
    608     *  
     668 
     669    /** 
     670    * Clears any cookies that have been added (DEPRECATED). 
     671    * 
    609672    * Useful for multiple request scenarios 
    610673    * 
     
    628691    { 
    629692        if (!is_a($this->_url, 'Net_URL')) { 
    630             return PEAR::raiseError('No URL given.'); 
     693            return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL); 
    631694        } 
    632695 
     
    634697        $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port; 
    635698 
    636         // 4.3.0 supports SSL connections using OpenSSL. The function test determines 
    637         // we running on at least 4.3.0 
    638         if (strcasecmp($this->_url->protocol, 'https') == 0 AND function_exists('file_get_contents') AND extension_loaded('openssl')) { 
    639             if (isset($this->_proxy_host)) { 
    640                 return PEAR::raiseError('HTTPS proxies are not supported.'); 
     699        if (strcasecmp($this->_url->protocol, 'https') == 0) { 
     700            // Bug #14127, don't try connecting to HTTPS sites without OpenSSL 
     701            if (version_compare(PHP_VERSION, '4.3.0', '<') || !extension_loaded('openssl')) { 
     702                return PEAR::raiseError('Need PHP 4.3.0 or later with OpenSSL support for https:// requests', 
     703                                        HTTP_REQUEST_ERROR_URL); 
     704            } elseif (isset($this->_proxy_host)) { 
     705                return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY); 
    641706            } 
    642707            $host = 'ssl://' . $host; 
     
    647712        ini_set('magic_quotes_runtime', false); 
    648713 
    649         // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive  
     714        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive 
    650715        // connection token to a proxy server... 
    651716        if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) && 
     
    663728        // There is a connected socket in the "static" property? 
    664729        if ($keepAlive && !empty($sockets[$sockKey]) && 
    665             !empty($sockets[$sockKey]->fp))  
     730            !empty($sockets[$sockKey]->fp)) 
    666731        { 
    667732            $this->_sock =& $sockets[$sockKey]; 
     
    722787            AND !empty($this->_response->_headers['location'])) { 
    723788 
    724              
     789 
    725790            $redirect = $this->_response->_headers['location']; 
    726791 
     
    732797            } elseif ($redirect{0} == '/') { 
    733798                $this->_url->path = $redirect; 
    734              
     799 
    735800            // Relative path 
    736801            } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') { 
     
    742807                $redirect = Net_URL::resolvePath($redirect); 
    743808                $this->_url->path = $redirect; 
    744                  
     809 
    745810            // Filename, no path 
    746811            } else { 
     
    758823        // Too many redirects 
    759824        } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) { 
    760             return PEAR::raiseError('Too many redirects'); 
     825            return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS); 
    761826        } 
    762827 
     
    789854 
    790855    /** 
     856    * Returns the response reason phrase 
     857    * 
     858    * @access public 
     859    * @return mixed     Response reason phrase, false if not set 
     860    */ 
     861    function getResponseReason() 
     862    { 
     863        return isset($this->_response->_reason) ? $this->_response->_reason : false; 
     864    } 
     865 
     866    /** 
    791867    * Returns either the named header or all if no name given 
    792868    * 
     
    819895    /** 
    820896    * Returns cookies set in response 
    821     *  
     897    * 
    822898    * @access public 
    823899    * @return mixed     array of response cookies, false if none are present 
     
    846922        $url  = $host . $port . $path; 
    847923 
     924        if (!strlen($url)) { 
     925            $url = '/'; 
     926        } 
     927 
    848928        $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n"; 
    849929 
    850930        if (in_array($this->_method, $this->_bodyDisallowed) || 
    851             (empty($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method || 
     931            (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method || 
    852932             (empty($this->_postData) && empty($this->_postFiles))))) 
    853933        { 
     
    871951        } 
    872952 
    873         // No post data or wrong method, so simply add a final CRLF 
    874         if (in_array($this->_method, $this->_bodyDisallowed) ||  
    875             (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body))) { 
     953        // Method does not allow a body, simply add a final CRLF 
     954        if (in_array($this->_method, $this->_bodyDisallowed)) { 
    876955 
    877956            $request .= "\r\n"; 
    878957 
    879958        // Post data if it's an array 
    880         } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&  
     959        } elseif (HTTP_REQUEST_METHOD_POST == $this->_method && 
    881960                  (!empty($this->_postData) || !empty($this->_postFiles))) { 
    882961 
     
    884963            if (!isset($boundary)) { 
    885964                $postdata = implode('&', array_map( 
    886                     create_function('$a', 'return $a[0] . \'=\' . $a[1];'),  
     965                    create_function('$a', 'return $a[0] . \'=\' . $a[1];'), 
    887966                    $this->_flattenArray('', $this->_postData) 
    888967                )); 
     
    907986                    } 
    908987                    foreach ($value['name'] as $key => $filename) { 
    909                         $fp   = fopen($filename, 'r'); 
    910                         $data = fread($fp, filesize($filename)); 
    911                         fclose($fp); 
     988                        $fp       = fopen($filename, 'r'); 
    912989                        $basename = basename($filename); 
    913990                        $type     = is_array($value['type'])? @$value['type'][$key]: $value['type']; 
     
    916993                        $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"'; 
    917994                        $postdata .= "\r\nContent-Type: " . $type; 
    918                         $postdata .= "\r\n\r\n" . $data . "\r\n"; 
     995                        $postdata .= "\r\n\r\n" . fread($fp, filesize($filename)) . "\r\n"; 
     996                        fclose($fp); 
    919997                    } 
    920998                } 
    921999                $postdata .= '--' . $boundary . "--\r\n"; 
    9221000            } 
    923             $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n"; 
     1001            $request .= 'Content-Length: ' . 
     1002                        (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) . 
     1003                        "\r\n\r\n"; 
    9241004            $request .= $postdata; 
    9251005 
    9261006        // Explicitly set request body 
    927         } elseif (!empty($this->_body)) { 
    928  
    929             $request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n"; 
     1007        } elseif (0 < strlen($this->_body)) { 
     1008 
     1009            $request .= 'Content-Length: ' . 
     1010                        (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) . 
     1011                        "\r\n\r\n"; 
    9301012            $request .= $this->_body; 
    931         } 
    932          
     1013 
     1014        // No body: send a Content-Length header nonetheless (request #12900), 
     1015        // but do that only for methods that require a body (bug #14740) 
     1016        } else { 
     1017 
     1018            if (in_array($this->_method, $this->_bodyRequired)) { 
     1019                $request .= "Content-Length: 0\r\n"; 
     1020            } 
     1021            $request .= "\r\n"; 
     1022        } 
     1023 
    9331024        return $request; 
    9341025    } 
     
    9411032    * @param    mixed   item's values 
    9421033    * @return   array   array with the following items: array('item name', 'item value'); 
     1034    * @access   private 
    9431035    */ 
    9441036    function _flattenArray($name, $values) 
     
    9661058    * Adds a Listener to the list of listeners that are notified of 
    9671059    * the object's events 
    968     *  
    969     * @param    object   HTTP_Request_Listener instance to attach 
    970     * @return   boolean  whether the listener was successfully attached 
    971     * @access   public 
    972     */ 
    973     function attach(&$listener) 
    974     { 
    975         if (!is_a($listener, 'HTTP_Request_Listener')) { 
    976             return false; 
    977         } 
    978         $this->_listeners[$listener->getId()] =& $listener; 
    979         return true; 
    980     } 
    981  
    982  
    983    /** 
    984     * Removes a Listener from the list of listeners  
    985     *  
    986     * @param    object   HTTP_Request_Listener instance to detach 
    987     * @return   boolean  whether the listener was successfully detached 
    988     * @access   public 
    989     */ 
    990     function detach(&$listener) 
    991     { 
    992         if (!is_a($listener, 'HTTP_Request_Listener') ||  
    993             !isset($this->_listeners[$listener->getId()])) { 
    994             return false; 
    995         } 
    996         unset($this->_listeners[$listener->getId()]); 
    997         return true; 
    998     } 
    999  
    1000  
    1001    /** 
    1002     * Notifies all registered listeners of an event. 
    1003     *  
     1060    * 
    10041061    * Events sent by HTTP_Request object 
    10051062    * - 'connect': on connection to server 
    10061063    * - 'sentRequest': after the request was sent 
    10071064    * - 'disconnect': on disconnection from server 
    1008     *  
     1065    * 
    10091066    * Events sent by HTTP_Response object 
    10101067    * - 'gotHeaders': after receiving response headers (headers are passed in $data) 
     
    10121069    * - 'gzTick': on receiving a gzip-encoded part of response body (ditto) 
    10131070    * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped) 
    1014     *  
     1071    * 
     1072    * @param    HTTP_Request_Listener   listener to attach 
     1073    * @return   boolean                 whether the listener was successfully attached 
     1074    * @access   public 
     1075    */ 
     1076    function attach(&$listener) 
     1077    { 
     1078        if (!is_a($listener, 'HTTP_Request_Listener')) { 
     1079            return false; 
     1080        } 
     1081        $this->_listeners[$listener->getId()] =& $listener; 
     1082        return true; 
     1083    } 
     1084 
     1085 
     1086   /** 
     1087    * Removes a Listener from the list of listeners 
     1088    * 
     1089    * @param    HTTP_Request_Listener   listener to detach 
     1090    * @return   boolean                 whether the listener was successfully detached 
     1091    * @access   public 
     1092    */ 
     1093    function detach(&$listener) 
     1094    { 
     1095        if (!is_a($listener, 'HTTP_Request_Listener') || 
     1096            !isset($this->_listeners[$listener->getId()])) { 
     1097            return false; 
     1098        } 
     1099        unset($this->_listeners[$listener->getId()]); 
     1100        return true; 
     1101    } 
     1102 
     1103 
     1104   /** 
     1105    * Notifies all registered listeners of an event. 
     1106    * 
    10151107    * @param    string  Event name 
    10161108    * @param    mixed   Additional data 
    10171109    * @access   private 
     1110    * @see      HTTP_Request::attach() 
    10181111    */ 
    10191112    function _notify($event, $data = null) 
     
    10271120 
    10281121/** 
    1029 * Response class to complement the Request class 
    1030 */ 
     1122 * Response class to complement the Request class 
     1123 * 
     1124 * @category    HTTP 
     1125 * @package     HTTP_Request 
     1126 * @author      Richard Heyes <richard@phpguru.org> 
     1127 * @author      Alexey Borzov <avb@php.net> 
     1128 * @version     Release: 1.4.4 
     1129 */ 
    10311130class HTTP_Response 
    10321131{ 
    10331132    /** 
    10341133    * Socket object 
    1035     * @var object 
     1134    * @var Net_Socket 
    10361135    */ 
    10371136    var $_sock; 
     
    10421141    */ 
    10431142    var $_protocol; 
    1044      
     1143 
    10451144    /** 
    10461145    * Return code 
     
    10481147    */ 
    10491148    var $_code; 
    1050      
     1149 
     1150    /** 
     1151    * Response reason phrase 
     1152    * @var string 
     1153    */ 
     1154    var $_reason; 
     1155 
    10511156    /** 
    10521157    * Response headers 
     
    10561161 
    10571162    /** 
    1058     * Cookies set in response   
     1163    * Cookies set in response 
    10591164    * @var array 
    10601165    */ 
     
    10881193    * Constructor 
    10891194    * 
    1090     * @param  object Net_Socket     socket to read the response from 
    1091     * @param  array                 listeners attached to request 
    1092     * @return mixed PEAR Error on error, true otherwise 
     1195    * @param  Net_Socket    socket to read the response from 
     1196    * @param  array         listeners attached to request 
    10931197    */ 
    10941198    function HTTP_Response(&$sock, &$listeners) 
     
    11011205   /** 
    11021206    * Processes a HTTP response 
    1103     *  
    1104     * This extracts response code, headers, cookies and decodes body if it  
     1207    * 
     1208    * This extracts response code, headers, cookies and decodes body if it 
    11051209    * was encoded in some way 
    11061210    * 
     
    11181222        do { 
    11191223            $line = $this->_sock->readLine(); 
    1120             if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) { 
    1121                 return PEAR::raiseError('Malformed response.'); 
     1224            if (!preg_match('!^(HTTP/\d\.\d) (\d{3})(?: (.+))?!', $line, $s)) { 
     1225                return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE); 
    11221226            } else { 
    1123                 $this->_protocol = 'HTTP/' . $http_version; 
    1124                 $this->_code     = intval($returncode); 
     1227                $this->_protocol = $s[1]; 
     1228                $this->_code     = intval($s[2]); 
     1229                $this->_reason   = empty($s[3])? null: $s[3]; 
    11251230            } 
    11261231            while ('' !== ($header = $this->_sock->readLine())) { 
     
    11321237 
    11331238        // RFC 2616, section 4.4: 
    1134         // 1. Any response message which "MUST NOT" include a message-body ...  
    1135         // is always terminated by the first empty line after the header fields  
     1239        // 1. Any response message which "MUST NOT" include a message-body ... 
     1240        // is always terminated by the first empty line after the header fields 
    11361241        // 3. ... If a message is received with both a 
    11371242        // Transfer-Encoding header field and a Content-Length header field, 
    11381243        // the latter MUST be ignored. 
    1139         $canHaveBody = $canHaveBody && $this->_code >= 200 &&  
     1244        $canHaveBody = $canHaveBody && $this->_code >= 200 && 
    11401245                       $this->_code != 204 && $this->_code != 304; 
    11411246 
     
    11441249        $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']); 
    11451250        $hasBody = false; 
    1146         if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||  
     1251        if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) || 
    11471252                0 != $this->_headers['content-length'])) 
    11481253        { 
     
    11591264                } else { 
    11601265                    $data = $this->_sock->read(min(4096, $this->_toRead)); 
    1161                     $this->_toRead -= strlen($data); 
     1266                    $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data); 
    11621267                } 
    1163                 if ('' == $data) { 
     1268                if ('' == $data && (!$this->_chunkLength || $this->_sock->eof())) { 
    11641269                    break; 
    11651270                } else { 
     
    12041309        $headername  = strtolower($headername); 
    12051310        $headervalue = ltrim($headervalue); 
    1206          
     1311 
    12071312        if ('set-cookie' != $headername) { 
    12081313            if (isset($this->_headers[$headername])) { 
     
    12701375   /** 
    12711376    * Read a part of response body encoded with chunked Transfer-Encoding 
    1272     *  
     1377    * 
    12731378    * @access private 
    12741379    * @return string 
     
    12801385            $line = $this->_sock->readLine(); 
    12811386            if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) { 
    1282                 $this->_chunkLength = hexdec($matches[1]);  
     1387                $this->_chunkLength = hexdec($matches[1]); 
    12831388                // Chunk with zero length indicates the end 
    12841389                if (0 == $this->_chunkLength) { 
     
    12911396        } 
    12921397        $data = $this->_sock->read($this->_chunkLength); 
    1293         $this->_chunkLength -= strlen($data); 
     1398        $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data); 
    12941399        if (0 == $this->_chunkLength) { 
    12951400            $this->_sock->readLine(); // Trailing CRLF 
     
    13011406   /** 
    13021407    * Notifies all registered listeners of an event. 
    1303     *  
     1408    * 
    13041409    * @param    string  Event name 
    13051410    * @param    mixed   Additional data 
     
    13201425    * The real decoding work is done by gzinflate() built-in function, this 
    13211426    * method only parses the header and checks data for compliance with 
    1322     * RFC 1952   
     1427    * RFC 1952 
    13231428    * 
    13241429    * @access   private 
     
    13281433    function _decodeGzip($data) 
    13291434    { 
     1435        if (HTTP_REQUEST_MBSTRING) { 
     1436            $oldEncoding = mb_internal_encoding(); 
     1437            mb_internal_encoding('iso-8859-1'); 
     1438        } 
    13301439        $length = strlen($data); 
    13311440        // If it doesn't look like gzip-encoded data, don't bother 
     
    13351444        $method = ord(substr($data, 2, 1)); 
    13361445        if (8 != $method) { 
    1337             return PEAR::raiseError('_decodeGzip(): unknown compression method'); 
     1446            return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD); 
    13381447        } 
    13391448        $flags = ord(substr($data, 3, 1)); 
    13401449        if ($flags & 224) { 
    1341             return PEAR::raiseError('_decodeGzip(): reserved bits are set'); 
     1450            return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13421451        } 
    13431452 
     
    13471456        if ($flags & 4) { 
    13481457            if ($length - $headerLength - 2 < 8) { 
    1349                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1458                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13501459            } 
    13511460            $extraLength = unpack('v', substr($data, 10, 2)); 
    13521461            if ($length - $headerLength - 2 - $extraLength[1] < 8) { 
    1353                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1462                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13541463            } 
    13551464            $headerLength += $extraLength[1] + 2; 
     
    13581467        if ($flags & 8) { 
    13591468            if ($length - $headerLength - 1 < 8) { 
    1360                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1469                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13611470            } 
    13621471            $filenameLength = strpos(substr($data, $headerLength), chr(0)); 
    13631472            if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) { 
    1364                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1473                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13651474            } 
    13661475            $headerLength += $filenameLength + 1; 
     
    13691478        if ($flags & 16) { 
    13701479            if ($length - $headerLength - 1 < 8) { 
    1371                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1480                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13721481            } 
    13731482            $commentLength = strpos(substr($data, $headerLength), chr(0)); 
    13741483            if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) { 
    1375                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1484                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13761485            } 
    13771486            $headerLength += $commentLength + 1; 
     
    13801489        if ($flags & 1) { 
    13811490            if ($length - $headerLength - 2 < 8) { 
    1382                 return PEAR::raiseError('_decodeGzip(): data too short'); 
     1491                return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA); 
    13831492            } 
    13841493            $crcReal   = 0xffff & crc32(substr($data, 0, $headerLength)); 
    13851494            $crcStored = unpack('v', substr($data, $headerLength, 2)); 
    13861495            if ($crcReal != $crcStored[1]) { 
    1387                 return PEAR::raiseError('_decodeGzip(): header CRC check failed'); 
     1496                return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC); 
    13881497            } 
    13891498            $headerLength += 2; 
     
    13951504 
    13961505        // finally, call the gzinflate() function 
    1397         $unpacked = @gzinflate(substr($data, $headerLength, -8), $dataSize); 
     1506        // don't pass $dataSize to gzinflate, see bugs #13135, #14370 
     1507        $unpacked = gzinflate(substr($data, $headerLength, -8)); 
    13981508        if (false === $unpacked) { 
    1399             return PEAR::raiseError('_decodeGzip(): gzinflate() call failed'); 
     1509            return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ); 
    14001510        } elseif ($dataSize != strlen($unpacked)) { 
    1401             return PEAR::raiseError('_decodeGzip(): data size check failed'); 
    1402         } elseif ($dataCrc != crc32($unpacked)) { 
    1403             return PEAR::raiseError('_decodeGzip(): data CRC check failed'); 
     1511            return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ); 
     1512        } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) { 
     1513            return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC); 
     1514        } 
     1515        if (HTTP_REQUEST_MBSTRING) { 
     1516            mb_internal_encoding($oldEncoding); 
    14041517        } 
    14051518        return $unpacked; 
  • branches/version-2_11-dev/data/module/Net/URL.php

    r20116 r21237  
    3737// Net_URL Class 
    3838 
     39 
    3940class Net_URL 
    4041{ 
     42    var $options = array('encode_query_keys' => false); 
    4143    /** 
    4244    * Full url 
     
    122124    function __construct($url = null, $useBrackets = true) 
    123125    { 
     126        $this->url = $url; 
     127        $this->useBrackets = $useBrackets; 
     128 
     129        $this->initialize(); 
     130    } 
     131 
     132    function initialize() 
     133    { 
    124134        $HTTP_SERVER_VARS  = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS']; 
    125135 
    126         $this->useBrackets = $useBrackets; 
    127         $this->url         = $url; 
    128136        $this->user        = ''; 
    129137        $this->pass        = ''; 
     
    135143 
    136144        // Only use defaults if not an absolute URL given 
    137         if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) { 
    138  
    139             $this->protocol    = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http'); 
     145        if (!preg_match('/^[a-z0-9]+:\/\//i', $this->url)) { 
     146            $this->protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http'); 
    140147 
    141148            /** 
    142149            * Figure out host/port 
    143150            */ 
    144             if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) { 
     151            if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) &&  
     152                preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches))  
     153            { 
    145154                $host = $matches[1]; 
    146155                if (!empty($matches[3])) { 
     
    161170 
    162171        // Parse the url and store the various parts 
    163         if (!empty($url)) { 
    164             $urlinfo = parse_url($url); 
     172        if (!empty($this->url)) { 
     173            $urlinfo = parse_url($this->url); 
    165174 
    166175            // Default querystring 
     
    201210        } 
    202211    } 
    203  
    204212    /** 
    205213    * Returns full url 
     
    224232 
    225233    /** 
    226     * Adds a querystring item 
     234    * Adds or updates a querystring item (URL parameter). 
     235    * Automatically encodes parameters with rawurlencode() if $preencoded 
     236    *  is false. 
     237    * You can pass an array to $value, it gets mapped via [] in the URL if 
     238    * $this->useBrackets is activated. 
    227239    * 
    228240    * @param  string $name       Name of item 
     
    233245    function addQueryString($name, $value, $preencoded = false) 
    234246    { 
     247        if ($this->getOption('encode_query_keys')) { 
     248            $name = rawurlencode($name); 
     249        } 
     250 
    235251        if ($preencoded) { 
    236252            $this->querystring[$name] = $value; 
     
    248264    function removeQueryString($name) 
    249265    { 
     266        if ($this->getOption('encode_query_keys')) { 
     267            $name = rawurlencode($name); 
     268        } 
     269 
    250270        if (isset($this->querystring[$name])) { 
    251271            unset($this->querystring[$name]); 
     
    274294        if (!empty($this->querystring)) { 
    275295            foreach ($this->querystring as $name => $value) { 
     296                // Encode var name 
     297                $name = rawurlencode($name); 
     298 
    276299                if (is_array($value)) { 
    277300                    foreach ($value as $k => $v) { 
     
    312335                $key   = $part; 
    313336            } 
    314             if (substr($key, -2) == '[]') { 
    315                 $key = substr($key, 0, -2); 
    316                 if (@!is_array($return[$key])) { 
    317                     $return[$key]   = array(); 
     337 
     338            if (!$this->getOption('encode_query_keys')) { 
     339                $key = rawurldecode($key); 
     340            } 
     341 
     342            if (preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) { 
     343                $key = $matches[1]; 
     344                $idx = $matches[2]; 
     345 
     346                // Ensure is an array 
     347                if (empty($return[$key]) || !is_array($return[$key])) { 
     348                    $return[$key] = array(); 
     349                } 
     350 
     351                // Add data 
     352                if ($idx === '') { 
    318353                    $return[$key][] = $value; 
    319354                } else { 
    320                     $return[$key][] = $value; 
     355                    $return[$key][$idx] = $value; 
    321356                } 
    322357            } elseif (!$this->useBrackets AND !empty($return[$key])) { 
     
    341376    * This method can also be called statically. 
    342377    * 
    343     * @param  string $url URL path to resolve 
     378    * @param  string $path URL path to resolve 
    344379    * @return string      The result 
    345380    */ 
     
    404439    { 
    405440        $this->protocol = $protocol; 
    406         $this->port = is_null($port) ? $this->getStandardPort() : $port; 
     441        $this->port     = is_null($port) ? $this->getStandardPort($protocol) : $port; 
     442    } 
     443 
     444    /** 
     445     * Set an option 
     446     * 
     447     * This function set an option 
     448     * to be used thorough the script. 
     449     * 
     450     * @access public 
     451     * @param  string $optionName  The optionname to set 
     452     * @param  string $value       The value of this option. 
     453     */ 
     454    function setOption($optionName, $value) 
     455    { 
     456        if (!array_key_exists($optionName, $this->options)) { 
     457            return false; 
     458        } 
     459 
     460        $this->options[$optionName] = $value; 
     461        $this->initialize(); 
     462    } 
     463 
     464    /** 
     465     * Get an option 
     466     * 
     467     * This function gets an option 
     468     * from the $this->options array 
     469     * and return it's value. 
     470     * 
     471     * @access public 
     472     * @param  string $opionName  The name of the option to retrieve 
     473     * @see    $this->options 
     474     */ 
     475    function getOption($optionName) 
     476    { 
     477        if (!isset($this->options[$optionName])) { 
     478            return false; 
     479        } 
     480 
     481        return $this->options[$optionName]; 
    407482    } 
    408483 
  • branches/version-2_11-dev/html/install/index.php

    r21185 r21237  
    3232 
    3333$ownDir = realpath(dirname(__FILE__)) . '/'; 
    34 require_once DATA_REALDIR . 'module/Request.php'; 
     34require_once DATA_REALDIR . 'module/HTTP/Request.php'; 
    3535 
    3636if(!defined("ADMIN_DIR")){ 
Note: See TracChangeset for help on using the changeset viewer.