Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

   1  <?php
   2  
   3  namespace IMSGlobal\LTI\OAuth;
   4  
   5  /**
   6   * Class to provide %OAuth utility methods
   7   *
   8   * @copyright  Andy Smith
   9   * @version 2008-08-04
  10   * @license https://opensource.org/licenses/MIT The MIT License
  11   */
  12  #[\AllowDynamicProperties]
  13  class OAuthUtil {
  14  
  15      public static function urlencode_rfc3986($input) {
  16  
  17          if (is_array($input)) {
  18              return array_map(array('IMSGlobal\LTI\OAuth\OAuthUtil', 'urlencode_rfc3986'), $input);
  19          } else if (is_scalar($input)) {
  20              return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($input)));
  21          } else {
  22              return '';
  23          }
  24      }
  25  
  26      // This decode function isn't taking into consideration the above
  27      // modifications to the encoding process. However, this method doesn't
  28      // seem to be used anywhere so leaving it as is.
  29      public static function urldecode_rfc3986($string) {
  30          return urldecode($string);
  31      }
  32  
  33      // Utility function for turning the Authorization: header into
  34      // parameters, has to do some unescaping
  35      // Can filter out any non-oauth parameters if needed (default behaviour)
  36      // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
  37      //                  see http://code.google.com/p/oauth/issues/detail?id=163
  38      public static function split_header($header, $only_allow_oauth_parameters = true) {
  39  
  40          $params = array();
  41          if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
  42              foreach ($matches[1] as $i => $h) {
  43                  $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
  44              }
  45              if (isset($params['realm'])) {
  46                  unset($params['realm']);
  47              }
  48          }
  49  
  50          return $params;
  51  
  52      }
  53  
  54      // helper to try to sort out headers for people who aren't running apache
  55      public static function get_headers() {
  56  
  57          if (function_exists('apache_request_headers')) {
  58              // we need this to get the actual Authorization: header
  59              // because apache tends to tell us it doesn't exist
  60              $headers = apache_request_headers();
  61  
  62              // sanitize the output of apache_request_headers because
  63              // we always want the keys to be Cased-Like-This and arh()
  64              // returns the headers in the same case as they are in the
  65              // request
  66              $out = array();
  67              foreach ($headers AS $key => $value) {
  68                  $key = str_replace(" ", "-", ucwords(strtolower(str_replace("-", " ", $key))));
  69                  $out[$key] = $value;
  70              }
  71          } else {
  72              // otherwise we don't have apache and are just going to have to hope
  73              // that $_SERVER actually contains what we need
  74              $out = array();
  75              if( isset($_SERVER['CONTENT_TYPE']) )
  76                  $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
  77              if( isset($_ENV['CONTENT_TYPE']) )
  78                  $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
  79  
  80              foreach ($_SERVER as $key => $value) {
  81                  if (substr($key, 0, 5) == 'HTTP_') {
  82                      // this is chaos, basically it is just there to capitalize the first
  83                      // letter of every word that is not an initial HTTP and strip HTTP
  84                      // code from przemek
  85                      $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
  86                      $out[$key] = $value;
  87                  }
  88              }
  89          }
  90          return $out;
  91      }
  92  
  93      // This function takes a input like a=b&a=c&d=e and returns the parsed
  94      // parameters like this
  95      // array('a' => array('b','c'), 'd' => 'e')
  96      public static function parse_parameters( $input ) {
  97  
  98          if (!isset($input) || !$input) return array();
  99  
 100          $pairs = explode('&', $input);
 101  
 102          $parsed_parameters = array();
 103          foreach ($pairs as $pair) {
 104              $split = explode('=', $pair, 2);
 105              $parameter = self::urldecode_rfc3986($split[0]);
 106              $value = isset($split[1]) ? self::urldecode_rfc3986($split[1]) : '';
 107  
 108              if (isset($parsed_parameters[$parameter])) {
 109                  // We have already recieved parameter(s) with this name, so add to the list
 110                  // of parameters with this name
 111  
 112                  if (is_scalar($parsed_parameters[$parameter])) {
 113                      // This is the first duplicate, so transform scalar (string) into an array
 114                      // so we can add the duplicates
 115                      $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
 116                  }
 117  
 118                  $parsed_parameters[$parameter][] = $value;
 119              } else {
 120                  $parsed_parameters[$parameter] = $value;
 121              }
 122          }
 123  
 124          return $parsed_parameters;
 125  
 126      }
 127  
 128      public static function build_http_query($params) {
 129  
 130          if (!$params) return '';
 131  
 132          // Urlencode both keys and values
 133          $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
 134          $values = OAuthUtil::urlencode_rfc3986(array_values($params));
 135          $params = array_combine($keys, $values);
 136  
 137          // Parameters are sorted by name, using lexicographical byte value ordering.
 138          // Ref: Spec: 9.1.1 (1)
 139          uksort($params, 'strcmp');
 140  
 141          $pairs = array();
 142          foreach ($params as $parameter => $value) {
 143              if (is_array($value)) {
 144                  // If two or more parameters share the same name, they are sorted by their value
 145                  // Ref: Spec: 9.1.1 (1)
 146                  // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
 147                  sort($value, SORT_STRING);
 148                  foreach ($value as $duplicate_value) {
 149                      $pairs[] = $parameter . '=' . $duplicate_value;
 150                  }
 151              } else {
 152                  $pairs[] = $parameter . '=' . $value;
 153              }
 154          }
 155  
 156          // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
 157          // Each name-value pair is separated by an '&' character (ASCII code 38)
 158          return implode('&', $pairs);
 159  
 160      }
 161  
 162  }