Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/lti/OAuth.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of BasicLTI4Moodle
00003 //
00004 // BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability)
00005 // consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web
00006 // based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI
00007 // specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS
00008 // are already supporting or going to support BasicLTI. This project Implements the consumer
00009 // for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas.
00010 // BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem
00011 // at the GESSI research group at UPC.
00012 // SimpleLTI consumer for Moodle is an implementation of the early specification of LTI
00013 // by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a
00014 // Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier.
00015 //
00016 // BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis
00017 // of the Universitat Politecnica de Catalunya http://www.upc.edu
00018 // Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu
00019 //
00020 // OAuth.php is distributed under the MIT License
00021 //
00022 // The MIT License
00023 //
00024 // Copyright (c) 2007 Andy Smith
00025 //
00026 // Permission is hereby granted, free of charge, to any person obtaining a copy
00027 // of this software and associated documentation files (the "Software"), to deal
00028 // in the Software without restriction, including without limitation the rights
00029 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00030 // copies of the Software, and to permit persons to whom the Software is
00031 // furnished to do so, subject to the following conditions:
00032 //
00033 // The above copyright notice and this permission notice shall be included in
00034 // all copies or substantial portions of the Software.
00035 //
00036 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00037 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00038 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00039 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00040 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00041 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00042 // THE SOFTWARE.
00043 //
00044 // Moodle is free software: you can redistribute it and/or modify
00045 // it under the terms of the GNU General Public License as published by
00046 // the Free Software Foundation, either version 3 of the License, or
00047 // (at your option) any later version.
00048 //
00049 // Moodle is distributed in the hope that it will be useful,
00050 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00051 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00052 // GNU General Public License for more details.
00053 //
00054 // You should have received a copy of the GNU General Public License
00055 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
00056 
00057 namespace moodle\mod\lti;//Using a namespace as the basicLTI module imports classes with the same names
00058 
00059 defined('MOODLE_INTERNAL') || die;
00060 
00061 $oauth_last_computed_signature = false;
00062 
00063 /* Generic exception class
00064  */
00065 class OAuthException extends \Exception {
00066     // pass
00067 }
00068 
00069 class OAuthConsumer {
00070     public $key;
00071     public $secret;
00072 
00073     function __construct($key, $secret, $callback_url = null) {
00074         $this->key = $key;
00075         $this->secret = $secret;
00076         $this->callback_url = $callback_url;
00077     }
00078 
00079     function __toString() {
00080         return "OAuthConsumer[key=$this->key,secret=$this->secret]";
00081     }
00082 }
00083 
00084 class OAuthToken {
00085     // access tokens and request tokens
00086     public $key;
00087     public $secret;
00088 
00093     function __construct($key, $secret) {
00094         $this->key = $key;
00095         $this->secret = $secret;
00096     }
00097 
00102     function to_string() {
00103         return "oauth_token=" .
00104         OAuthUtil::urlencode_rfc3986($this->key) .
00105         "&oauth_token_secret=" .
00106         OAuthUtil::urlencode_rfc3986($this->secret);
00107     }
00108 
00109     function __toString() {
00110         return $this->to_string();
00111     }
00112 }
00113 
00114 class OAuthSignatureMethod {
00115     public function check_signature(&$request, $consumer, $token, $signature) {
00116         $built = $this->build_signature($request, $consumer, $token);
00117         return $built == $signature;
00118     }
00119 }
00120 
00121 class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
00122     function get_name() {
00123         return "HMAC-SHA1";
00124     }
00125 
00126     public function build_signature($request, $consumer, $token) {
00127         global $oauth_last_computed_signature;
00128         $oauth_last_computed_signature = false;
00129 
00130         $base_string = $request->get_signature_base_string();
00131         $request->base_string = $base_string;
00132 
00133         $key_parts = array(
00134             $consumer->secret,
00135              ($token) ? $token->secret : ""
00136         );
00137 
00138         $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
00139         $key = implode('&', $key_parts);
00140 
00141         $computed_signature = base64_encode(hash_hmac('sha1', $base_string, $key, true));
00142         $oauth_last_computed_signature = $computed_signature;
00143         return $computed_signature;
00144     }
00145 
00146 }
00147 
00148 class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
00149     public function get_name() {
00150         return "PLAINTEXT";
00151     }
00152 
00153     public function build_signature($request, $consumer, $token) {
00154         $sig = array(
00155             OAuthUtil::urlencode_rfc3986($consumer->secret)
00156         );
00157 
00158         if ($token) {
00159             array_push($sig, OAuthUtil::urlencode_rfc3986($token->secret));
00160         } else {
00161             array_push($sig, '');
00162         }
00163 
00164         $raw = implode("&", $sig);
00165         // for debug purposes
00166         $request->base_string = $raw;
00167 
00168         return OAuthUtil::urlencode_rfc3986($raw);
00169     }
00170 }
00171 
00172 class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
00173     public function get_name() {
00174         return "RSA-SHA1";
00175     }
00176 
00177     protected function fetch_public_cert(&$request) {
00178         // not implemented yet, ideas are:
00179         // (1) do a lookup in a table of trusted certs keyed off of consumer
00180         // (2) fetch via http using a url provided by the requester
00181         // (3) some sort of specific discovery code based on request
00182         //
00183         // either way should return a string representation of the certificate
00184         throw Exception("fetch_public_cert not implemented");
00185     }
00186 
00187     protected function fetch_private_cert(&$request) {
00188         // not implemented yet, ideas are:
00189         // (1) do a lookup in a table of trusted certs keyed off of consumer
00190         //
00191         // either way should return a string representation of the certificate
00192         throw Exception("fetch_private_cert not implemented");
00193     }
00194 
00195     public function build_signature(&$request, $consumer, $token) {
00196         $base_string = $request->get_signature_base_string();
00197         $request->base_string = $base_string;
00198 
00199         // Fetch the private key cert based on the request
00200         $cert = $this->fetch_private_cert($request);
00201 
00202         // Pull the private key ID from the certificate
00203         $privatekeyid = openssl_get_privatekey($cert);
00204 
00205         // Sign using the key
00206         $ok = openssl_sign($base_string, $signature, $privatekeyid);
00207 
00208         // Release the key resource
00209         openssl_free_key($privatekeyid);
00210 
00211         return base64_encode($signature);
00212     }
00213 
00214     public function check_signature(&$request, $consumer, $token, $signature) {
00215         $decoded_sig = base64_decode($signature);
00216 
00217         $base_string = $request->get_signature_base_string();
00218 
00219         // Fetch the public key cert based on the request
00220         $cert = $this->fetch_public_cert($request);
00221 
00222         // Pull the public key ID from the certificate
00223         $publickeyid = openssl_get_publickey($cert);
00224 
00225         // Check the computed signature against the one passed in the query
00226         $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
00227 
00228         // Release the key resource
00229         openssl_free_key($publickeyid);
00230 
00231         return $ok == 1;
00232     }
00233 }
00234 
00235 class OAuthRequest {
00236     private $parameters;
00237     private $http_method;
00238     private $http_url;
00239     // for debug purposes
00240     public $base_string;
00241     public static $version = '1.0';
00242     public static $POST_INPUT = 'php://input';
00243 
00244     function __construct($http_method, $http_url, $parameters = null) {
00245         @$parameters or $parameters = array();
00246         $this->parameters = $parameters;
00247         $this->http_method = $http_method;
00248         $this->http_url = $http_url;
00249     }
00250 
00254     public static function from_request($http_method = null, $http_url = null, $parameters = null) {
00255         $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https';
00256         $port = "";
00257         if ($_SERVER['SERVER_PORT'] != "80" && $_SERVER['SERVER_PORT'] != "443" && strpos(':', $_SERVER['HTTP_HOST']) < 0) {
00258             $port = ':' . $_SERVER['SERVER_PORT'];
00259         }
00260         @$http_url or $http_url = $scheme .
00261         '://' . $_SERVER['HTTP_HOST'] .
00262         $port .
00263         $_SERVER['REQUEST_URI'];
00264         @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
00265 
00266         // We weren't handed any parameters, so let's find the ones relevant to
00267         // this request.
00268         // If you run XML-RPC or similar you should use this to provide your own
00269         // parsed parameter-list
00270         if (!$parameters) {
00271             // Find request headers
00272             $request_headers = OAuthUtil::get_headers();
00273 
00274             // Parse the query-string to find GET parameters
00275             $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
00276 
00277             $ourpost = $_POST;
00278             // Deal with magic_quotes
00279             // http://www.php.net/manual/en/security.magicquotes.disabling.php
00280             if (get_magic_quotes_gpc()) {
00281                 $outpost = array();
00282                 foreach ($_POST as $k => $v) {
00283                     $v = stripslashes($v);
00284                     $ourpost[$k] = $v;
00285                 }
00286             }
00287             // Add POST Parameters if they exist
00288             $parameters = array_merge($parameters, $ourpost);
00289 
00290             // We have a Authorization-header with OAuth data. Parse the header
00291             // and add those overriding any duplicates from GET or POST
00292             if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
00293                 $header_parameters = OAuthUtil::split_header($request_headers['Authorization']);
00294                 $parameters = array_merge($parameters, $header_parameters);
00295             }
00296 
00297         }
00298 
00299         return new OAuthRequest($http_method, $http_url, $parameters);
00300     }
00301 
00305     public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = null) {
00306         @$parameters or $parameters = array();
00307         $defaults = array(
00308             "oauth_version" => self::$version,
00309             "oauth_nonce" => self::generate_nonce(),
00310             "oauth_timestamp" => self::generate_timestamp(),
00311             "oauth_consumer_key" => $consumer->key
00312         );
00313         if ($token) {
00314             $defaults['oauth_token'] = $token->key;
00315         }
00316 
00317         $parameters = array_merge($defaults, $parameters);
00318 
00319         // Parse the query-string to find and add GET parameters
00320         $parts = parse_url($http_url);
00321         if (isset($parts['query'])) {
00322             $qparms = OAuthUtil::parse_parameters($parts['query']);
00323             $parameters = array_merge($qparms, $parameters);
00324         }
00325 
00326         return new OAuthRequest($http_method, $http_url, $parameters);
00327     }
00328 
00329     public function set_parameter($name, $value, $allow_duplicates = true) {
00330         if ($allow_duplicates && isset($this->parameters[$name])) {
00331             // We have already added parameter(s) with this name, so add to the list
00332             if (is_scalar($this->parameters[$name])) {
00333                 // This is the first duplicate, so transform scalar (string)
00334                 // into an array so we can add the duplicates
00335                 $this->parameters[$name] = array($this->parameters[$name]);
00336             }
00337 
00338             $this->parameters[$name][] = $value;
00339         } else {
00340             $this->parameters[$name] = $value;
00341         }
00342     }
00343 
00344     public function get_parameter($name) {
00345         return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
00346     }
00347 
00348     public function get_parameters() {
00349         return $this->parameters;
00350     }
00351 
00352     public function unset_parameter($name) {
00353         unset($this->parameters[$name]);
00354     }
00355 
00360     public function get_signable_parameters() {
00361         // Grab all parameters
00362         $params = $this->parameters;
00363 
00364         // Remove oauth_signature if present
00365         // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
00366         if (isset($params['oauth_signature'])) {
00367             unset($params['oauth_signature']);
00368         }
00369 
00370         return OAuthUtil::build_http_query($params);
00371     }
00372 
00380     public function get_signature_base_string() {
00381         $parts = array(
00382             $this->get_normalized_http_method(),
00383             $this->get_normalized_http_url(),
00384             $this->get_signable_parameters()
00385         );
00386 
00387         $parts = OAuthUtil::urlencode_rfc3986($parts);
00388 
00389         return implode('&', $parts);
00390     }
00391 
00395     public function get_normalized_http_method() {
00396         return strtoupper($this->http_method);
00397     }
00398 
00403     public function get_normalized_http_url() {
00404         $parts = parse_url($this->http_url);
00405 
00406         $port = @$parts['port'];
00407         $scheme = $parts['scheme'];
00408         $host = $parts['host'];
00409         $path = @$parts['path'];
00410 
00411         $port or $port = ($scheme == 'https') ? '443' : '80';
00412 
00413         if (($scheme == 'https' && $port != '443') || ($scheme == 'http' && $port != '80')) {
00414             $host = "$host:$port";
00415         }
00416         return "$scheme://$host$path";
00417     }
00418 
00422     public function to_url() {
00423         $post_data = $this->to_postdata();
00424         $out = $this->get_normalized_http_url();
00425         if ($post_data) {
00426             $out .= '?'.$post_data;
00427         }
00428         return $out;
00429     }
00430 
00434     public function to_postdata() {
00435         return OAuthUtil::build_http_query($this->parameters);
00436     }
00437 
00441     public function to_header() {
00442         $out = 'Authorization: OAuth realm=""';
00443         $total = array();
00444         foreach ($this->parameters as $k => $v) {
00445             if (substr($k, 0, 5) != "oauth") {
00446                 continue;
00447             }
00448             if (is_array($v)) {
00449                 throw new OAuthException('Arrays not supported in headers');
00450             }
00451             $out .= ',' .
00452             OAuthUtil::urlencode_rfc3986($k) .
00453             '="' .
00454             OAuthUtil::urlencode_rfc3986($v) .
00455             '"';
00456         }
00457         return $out;
00458     }
00459 
00460     public function __toString() {
00461         return $this->to_url();
00462     }
00463 
00464     public function sign_request($signature_method, $consumer, $token) {
00465         $this->set_parameter("oauth_signature_method", $signature_method->get_name(), false);
00466         $signature = $this->build_signature($signature_method, $consumer, $token);
00467         $this->set_parameter("oauth_signature", $signature, false);
00468     }
00469 
00470     public function build_signature($signature_method, $consumer, $token) {
00471         $signature = $signature_method->build_signature($this, $consumer, $token);
00472         return $signature;
00473     }
00474 
00478     private static function generate_timestamp() {
00479         return time();
00480     }
00481 
00485     private static function generate_nonce() {
00486         $mt = microtime();
00487         $rand = mt_rand();
00488 
00489         return md5($mt.$rand); // md5s look nicer than numbers
00490     }
00491 }
00492 
00493 class OAuthServer {
00494     protected $timestamp_threshold = 300; // in seconds, five minutes
00495     protected $version = 1.0; // hi blaine
00496     protected $signature_methods = array();
00497     protected $data_store;
00498 
00499     function __construct($data_store) {
00500         $this->data_store = $data_store;
00501     }
00502 
00503     public function add_signature_method($signature_method) {
00504         $this->signature_methods[$signature_method->get_name()] = $signature_method;
00505     }
00506 
00507     // high level functions
00508 
00513     public function fetch_request_token(&$request) {
00514         $this->get_version($request);
00515 
00516         $consumer = $this->get_consumer($request);
00517 
00518         // no token required for the initial token request
00519         $token = null;
00520 
00521         $this->check_signature($request, $consumer, $token);
00522 
00523         $new_token = $this->data_store->new_request_token($consumer);
00524 
00525         return $new_token;
00526     }
00527 
00532     public function fetch_access_token(&$request) {
00533         $this->get_version($request);
00534 
00535         $consumer = $this->get_consumer($request);
00536 
00537         // requires authorized request token
00538         $token = $this->get_token($request, $consumer, "request");
00539 
00540         $this->check_signature($request, $consumer, $token);
00541 
00542         $new_token = $this->data_store->new_access_token($token, $consumer);
00543 
00544         return $new_token;
00545     }
00546 
00550     public function verify_request(&$request) {
00551         global $oauth_last_computed_signature;
00552         $oauth_last_computed_signature = false;
00553         $this->get_version($request);
00554         $consumer = $this->get_consumer($request);
00555         $token = $this->get_token($request, $consumer, "access");
00556         $this->check_signature($request, $consumer, $token);
00557         return array(
00558             $consumer,
00559             $token
00560         );
00561     }
00562 
00563     // Internals from here
00567     private function get_version(&$request) {
00568         $version = $request->get_parameter("oauth_version");
00569         if (!$version) {
00570             $version = 1.0;
00571         }
00572         if ($version && $version != $this->version) {
00573             throw new OAuthException("OAuth version '$version' not supported");
00574         }
00575         return $version;
00576     }
00577 
00581     private function get_signature_method(&$request) {
00582         $signature_method = @ $request->get_parameter("oauth_signature_method");
00583         if (!$signature_method) {
00584             $signature_method = "PLAINTEXT";
00585         }
00586         if (!in_array($signature_method, array_keys($this->signature_methods))) {
00587             throw new OAuthException("Signature method '$signature_method' not supported " .
00588             "try one of the following: " .
00589             implode(", ", array_keys($this->signature_methods)));
00590         }
00591         return $this->signature_methods[$signature_method];
00592     }
00593 
00597     private function get_consumer(&$request) {
00598         $consumer_key = @ $request->get_parameter("oauth_consumer_key");
00599         if (!$consumer_key) {
00600             throw new OAuthException("Invalid consumer key");
00601         }
00602 
00603         $consumer = $this->data_store->lookup_consumer($consumer_key);
00604         if (!$consumer) {
00605             throw new OAuthException("Invalid consumer");
00606         }
00607 
00608         return $consumer;
00609     }
00610 
00614     private function get_token(&$request, $consumer, $token_type = "access") {
00615         $token_field = @ $request->get_parameter('oauth_token');
00616         if (!$token_field) {
00617             return false;
00618         }
00619         $token = $this->data_store->lookup_token($consumer, $token_type, $token_field);
00620         if (!$token) {
00621             throw new OAuthException("Invalid $token_type token: $token_field");
00622         }
00623         return $token;
00624     }
00625 
00630     private function check_signature(&$request, $consumer, $token) {
00631         // this should probably be in a different method
00632         global $oauth_last_computed_signature;
00633         $oauth_last_computed_signature = false;
00634 
00635         $timestamp = @ $request->get_parameter('oauth_timestamp');
00636         $nonce = @ $request->get_parameter('oauth_nonce');
00637 
00638         $this->check_timestamp($timestamp);
00639         $this->check_nonce($consumer, $token, $nonce, $timestamp);
00640 
00641         $signature_method = $this->get_signature_method($request);
00642 
00643         $signature = $request->get_parameter('oauth_signature');
00644         $valid_sig = $signature_method->check_signature($request, $consumer, $token, $signature);
00645 
00646         if (!$valid_sig) {
00647             $ex_text = "Invalid signature";
00648             if ($oauth_last_computed_signature) {
00649                 $ex_text = $ex_text . " ours= $oauth_last_computed_signature yours=$signature";
00650             }
00651             throw new OAuthException($ex_text);
00652         }
00653     }
00654 
00658     private function check_timestamp($timestamp) {
00659         // verify that timestamp is recentish
00660         $now = time();
00661         if ($now - $timestamp > $this->timestamp_threshold) {
00662             throw new OAuthException("Expired timestamp, yours $timestamp, ours $now");
00663         }
00664     }
00665 
00669     private function check_nonce($consumer, $token, $nonce, $timestamp) {
00670         // verify that the nonce is uniqueish
00671         $found = $this->data_store->lookup_nonce($consumer, $token, $nonce, $timestamp);
00672         if ($found) {
00673             throw new OAuthException("Nonce already used: $nonce");
00674         }
00675     }
00676 
00677 }
00678 
00679 class OAuthDataStore {
00680     function lookup_consumer($consumer_key) {
00681         // implement me
00682     }
00683 
00684     function lookup_token($consumer, $token_type, $token) {
00685         // implement me
00686     }
00687 
00688     function lookup_nonce($consumer, $token, $nonce, $timestamp) {
00689         // implement me
00690     }
00691 
00692     function new_request_token($consumer) {
00693         // return a new token attached to this consumer
00694     }
00695 
00696     function new_access_token($token, $consumer) {
00697         // return a new access token attached to this consumer
00698         // for the user associated with this token if the request token
00699         // is authorized
00700         // should also invalidate the request token
00701     }
00702 
00703 }
00704 
00705 class OAuthUtil {
00706     public static function urlencode_rfc3986($input) {
00707         if (is_array($input)) {
00708             return array_map(array(
00709                 'moodle\mod\lti\OAuthUtil',
00710                 'urlencode_rfc3986'
00711             ), $input);
00712         } else {
00713             if (is_scalar($input)) {
00714                 return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($input)));
00715             } else {
00716                 return '';
00717             }
00718         }
00719     }
00720 
00721     // This decode function isn't taking into consideration the above
00722     // modifications to the encoding process. However, this method doesn't
00723     // seem to be used anywhere so leaving it as is.
00724     public static function urldecode_rfc3986($string) {
00725         return urldecode($string);
00726     }
00727 
00728     // Utility function for turning the Authorization: header into
00729     // parameters, has to do some unescaping
00730     // Can filter out any non-oauth parameters if needed (default behaviour)
00731     public static function split_header($header, $only_allow_oauth_parameters = true) {
00732         $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
00733         $offset = 0;
00734         $params = array();
00735         while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
00736             $match = $matches[0];
00737             $header_name = $matches[2][0];
00738             $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
00739             if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
00740                 $params[$header_name] = self::urldecode_rfc3986($header_content);
00741             }
00742             $offset = $match[1] + strlen($match[0]);
00743         }
00744 
00745         if (isset($params['realm'])) {
00746             unset($params['realm']);
00747         }
00748 
00749         return $params;
00750     }
00751 
00752     // helper to try to sort out headers for people who aren't running apache
00753     public static function get_headers() {
00754         if (function_exists('apache_request_headers')) {
00755             // we need this to get the actual Authorization: header
00756             // because apache tends to tell us it doesn't exist
00757             return apache_request_headers();
00758         }
00759         // otherwise we don't have apache and are just going to have to hope
00760         // that $_SERVER actually contains what we need
00761         $out = array();
00762         foreach ($_SERVER as $key => $value) {
00763             if (substr($key, 0, 5) == "HTTP_") {
00764                 // this is chaos, basically it is just there to capitalize the first
00765                 // letter of every word that is not an initial HTTP and strip HTTP
00766                 // code from przemek
00767                 $key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5)))));
00768                 $out[$key] = $value;
00769             }
00770         }
00771         return $out;
00772     }
00773 
00774     // This function takes a input like a=b&a=c&d=e and returns the parsed
00775     // parameters like this
00776     // array('a' => array('b','c'), 'd' => 'e')
00777     public static function parse_parameters($input) {
00778         if (!isset($input) || !$input) {
00779             return array();
00780         }
00781 
00782         $pairs = explode('&', $input);
00783 
00784         $parsed_parameters = array();
00785         foreach ($pairs as $pair) {
00786             $split = explode('=', $pair, 2);
00787             $parameter = self::urldecode_rfc3986($split[0]);
00788             $value = isset($split[1]) ? self::urldecode_rfc3986($split[1]) : '';
00789 
00790             if (isset($parsed_parameters[$parameter])) {
00791                 // We have already recieved parameter(s) with this name, so add to the list
00792                 // of parameters with this name
00793 
00794                 if (is_scalar($parsed_parameters[$parameter])) {
00795                     // This is the first duplicate, so transform scalar (string) into an array
00796                     // so we can add the duplicates
00797                     $parsed_parameters[$parameter] = array(
00798                         $parsed_parameters[$parameter]
00799                     );
00800                 }
00801 
00802                 $parsed_parameters[$parameter][] = $value;
00803             } else {
00804                 $parsed_parameters[$parameter] = $value;
00805             }
00806         }
00807         return $parsed_parameters;
00808     }
00809 
00810     public static function build_http_query($params) {
00811         if (!$params) {
00812             return '';
00813         }
00814 
00815         // Urlencode both keys and values
00816         $keys = self::urlencode_rfc3986(array_keys($params));
00817         $values = self::urlencode_rfc3986(array_values($params));
00818         $params = array_combine($keys, $values);
00819 
00820         // Parameters are sorted by name, using lexicographical byte value ordering.
00821         // Ref: Spec: 9.1.1 (1)
00822         uksort($params, 'strcmp');
00823 
00824         $pairs = array();
00825         foreach ($params as $parameter => $value) {
00826             if (is_array($value)) {
00827                 // If two or more parameters share the same name, they are sorted by their value
00828                 // Ref: Spec: 9.1.1 (1)
00829                 natsort($value);
00830                 foreach ($value as $duplicate_value) {
00831                     $pairs[] = $parameter . '=' . $duplicate_value;
00832                 }
00833             } else {
00834                 $pairs[] = $parameter . '=' . $value;
00835             }
00836         }
00837         // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
00838         // Each name-value pair is separated by an '&' character (ASCII code 38)
00839         return implode('&', $pairs);
00840     }
00841 }
 All Data Structures Namespaces Files Functions Variables Enumerations