Add OpenID auth for editing pages
[busui.git] / lib / openid-php / Auth / OpenID.php
blob:a/lib/openid-php/Auth/OpenID.php -> blob:b/lib/openid-php/Auth/OpenID.php
  <?php
   
  /**
  * This is the PHP OpenID library by JanRain, Inc.
  *
  * This module contains core utility functionality used by the
  * library. See Consumer.php and Server.php for the consumer and
  * server implementations.
  *
  * PHP versions 4 and 5
  *
  * LICENSE: See the COPYING file included in this distribution.
  *
  * @package OpenID
  * @author JanRain, Inc. <openid@janrain.com>
  * @copyright 2005-2008 Janrain, Inc.
  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
  */
   
  /**
  * The library version string
  */
  define('Auth_OpenID_VERSION', '2.2.2');
   
  /**
  * Require the fetcher code.
  */
  require_once "Auth/Yadis/PlainHTTPFetcher.php";
  require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
  require_once "Auth/OpenID/BigMath.php";
  require_once "Auth/OpenID/URINorm.php";
   
  /**
  * Status code returned by the server when the only option is to show
  * an error page, since we do not have enough information to redirect
  * back to the consumer. The associated value is an error message that
  * should be displayed on an HTML error page.
  *
  * @see Auth_OpenID_Server
  */
  define('Auth_OpenID_LOCAL_ERROR', 'local_error');
   
  /**
  * Status code returned when there is an error to return in key-value
  * form to the consumer. The caller should return a 400 Bad Request
  * response with content-type text/plain and the value as the body.
  *
  * @see Auth_OpenID_Server
  */
  define('Auth_OpenID_REMOTE_ERROR', 'remote_error');
   
  /**
  * Status code returned when there is a key-value form OK response to
  * the consumer. The value associated with this code is the
  * response. The caller should return a 200 OK response with
  * content-type text/plain and the value as the body.
  *
  * @see Auth_OpenID_Server
  */
  define('Auth_OpenID_REMOTE_OK', 'remote_ok');
   
  /**
  * Status code returned when there is a redirect back to the
  * consumer. The value is the URL to redirect back to. The caller
  * should return a 302 Found redirect with a Location: header
  * containing the URL.
  *
  * @see Auth_OpenID_Server
  */
  define('Auth_OpenID_REDIRECT', 'redirect');
   
  /**
  * Status code returned when the caller needs to authenticate the
  * user. The associated value is a {@link Auth_OpenID_ServerRequest}
  * object that can be used to complete the authentication. If the user
  * has taken some authentication action, use the retry() method of the
  * {@link Auth_OpenID_ServerRequest} object to complete the request.
  *
  * @see Auth_OpenID_Server
  */
  define('Auth_OpenID_DO_AUTH', 'do_auth');
   
  /**
  * Status code returned when there were no OpenID arguments
  * passed. This code indicates that the caller should return a 200 OK
  * response and display an HTML page that says that this is an OpenID
  * server endpoint.
  *
  * @see Auth_OpenID_Server
  */
  define('Auth_OpenID_DO_ABOUT', 'do_about');
   
  /**
  * Defines for regexes and format checking.
  */
  define('Auth_OpenID_letters',
  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
   
  define('Auth_OpenID_digits',
  "0123456789");
   
  define('Auth_OpenID_punct',
  "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
   
  Auth_OpenID_include_init();
   
  /**
  * The OpenID utility function class.
  *
  * @package OpenID
  * @access private
  */
  class Auth_OpenID {
   
  /**
  * Return true if $thing is an Auth_OpenID_FailureResponse object;
  * false if not.
  *
  * @access private
  */
  static function isFailure($thing)
  {
  return is_a($thing, 'Auth_OpenID_FailureResponse');
  }
   
  /**
  * Gets the query data from the server environment based on the
  * request method used. If GET was used, this looks at
  * $_SERVER['QUERY_STRING'] directly. If POST was used, this
  * fetches data from the special php://input file stream.
  *
  * Returns an associative array of the query arguments.
  *
  * Skips invalid key/value pairs (i.e. keys with no '=value'
  * portion).
  *
  * Returns an empty array if neither GET nor POST was used, or if
  * POST was used but php://input cannot be opened.
  *
  * See background:
  * http://lists.openidenabled.com/pipermail/dev/2007-March/000395.html
  *
  * @access private
  */
  static function getQuery($query_str=null)
  {
  $data = array();
   
  if ($query_str !== null) {
  $data = Auth_OpenID::params_from_string($query_str);
  } else if (!array_key_exists('REQUEST_METHOD', $_SERVER)) {
  // Do nothing.
  } else {
  // XXX HACK FIXME HORRIBLE.
  //
  // POSTing to a URL with query parameters is acceptable, but
  // we don't have a clean way to distinguish those parameters
  // when we need to do things like return_to verification
  // which only want to look at one kind of parameter. We're
  // going to emulate the behavior of some other environments
  // by defaulting to GET and overwriting with POST if POST
  // data is available.
  $data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']);
   
  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  $str = file_get_contents('php://input');
   
  if ($str === false) {
  $post = array();
  } else {
  $post = Auth_OpenID::params_from_string($str);
  }
   
  $data = array_merge($data, $post);
  }
  }
   
  return $data;
  }
   
  static function params_from_string($str)
  {
  $chunks = explode("&", $str);
   
  $data = array();
  foreach ($chunks as $chunk) {
  $parts = explode("=", $chunk, 2);
   
  if (count($parts) != 2) {
  continue;
  }
   
  list($k, $v) = $parts;
  $data[urldecode($k)] = urldecode($v);
  }
   
  return $data;
  }
   
  /**
  * Create dir_name as a directory if it does not exist. If it
  * exists, make sure that it is, in fact, a directory. Returns
  * true if the operation succeeded; false if not.
  *
  * @access private
  */
  static function ensureDir($dir_name)
  {
  if (is_dir($dir_name) || @mkdir($dir_name)) {
  return true;
  } else {
  $parent_dir = dirname($dir_name);
   
  // Terminal case; there is no parent directory to create.
  if ($parent_dir == $dir_name) {
  return true;
  }
   
  return (Auth_OpenID::ensureDir($parent_dir) && @mkdir($dir_name));
  }
  }
   
  /**
  * Adds a string prefix to all values of an array. Returns a new
  * array containing the prefixed values.
  *
  * @access private
  */
  static function addPrefix($values, $prefix)
  {
  $new_values = array();
  foreach ($values as $s) {
  $new_values[] = $prefix . $s;
  }
  return $new_values;
  }
   
  /**
  * Convenience function for getting array values. Given an array
  * $arr and a key $key, get the corresponding value from the array
  * or return $default if the key is absent.
  *
  * @access private
  */
  static function arrayGet($arr, $key, $fallback = null)
  {
  if (is_array($arr)) {
  if (array_key_exists($key, $arr)) {
  return $arr[$key];
  } else {
  return $fallback;
  }
  } else {
  trigger_error("Auth_OpenID::arrayGet (key = ".$key.") expected " .
  "array as first parameter, got " .
  gettype($arr), E_USER_WARNING);
   
  return false;
  }
  }
   
  /**
  * Replacement for PHP's broken parse_str.
  */
  static function parse_str($query)
  {
  if ($query === null) {
  return null;
  }
   
  $parts = explode('&', $query);
   
  $new_parts = array();
  for ($i = 0; $i < count($parts); $i++) {
  $pair = explode('=', $parts[$i]);
   
  if (count($pair) != 2) {
  continue;
  }
   
  list($key, $value) = $pair;
  $new_parts[urldecode($key)] = urldecode($value);
  }
   
  return $new_parts;
  }
   
  /**
  * Implements the PHP 5 'http_build_query' functionality.
  *
  * @access private
  * @param array $data Either an array key/value pairs or an array
  * of arrays, each of which holding two values: a key and a value,
  * sequentially.
  * @return string $result The result of url-encoding the key/value
  * pairs from $data into a URL query string
  * (e.g. "username=bob&id=56").
  */
  static function httpBuildQuery($data)
  {
  $pairs = array();
  foreach ($data as $key => $value) {
  if (is_array($value)) {
  $pairs[] = urlencode($value[0])."=".urlencode($value[1]);
  } else {
  $pairs[] = urlencode($key)."=".urlencode($value);
  }
  }
  return implode("&", $pairs);
  }
   
  /**
  * "Appends" query arguments onto a URL. The URL may or may not
  * already have arguments (following a question mark).
  *
  * @access private
  * @param string $url A URL, which may or may not already have
  * arguments.
  * @param array $args Either an array key/value pairs or an array of
  * arrays, each of which holding two values: a key and a value,
  * sequentially. If $args is an ordinary key/value array, the
  * parameters will be added to the URL in sorted alphabetical order;
  * if $args is an array of arrays, their order will be preserved.
  * @return string $url The original URL with the new parameters added.
  *
  */
  static function appendArgs($url, $args)
  {
  if (count($args) == 0) {
  return $url;
  }
   
  // Non-empty array; if it is an array of arrays, use
  // multisort; otherwise use sort.
  if (array_key_exists(0, $args) &&
  is_array($args[0])) {
  // Do nothing here.
  } else {
  $keys = array_keys($args);
  sort($keys);
  $new_args = array();
  foreach ($keys as $key) {
  $new_args[] = array($key, $args[$key]);
  }
  $args = $new_args;
  }
   
  $sep = '?';
  if (strpos($url, '?') !== false) {
  $sep = '&';
  }
   
  return $url . $sep . Auth_OpenID::httpBuildQuery($args);
  }
   
  /**
  * Implements python's urlunparse, which is not available in PHP.
  * Given the specified components of a URL, this function rebuilds
  * and returns the URL.
  *
  * @access private
  * @param string $scheme The scheme (e.g. 'http'). Defaults to 'http'.
  * @param string $host The host. Required.
  * @param string $port The port.
  * @param string $path The path.
  * @param string $query The query.
  * @param string $fragment The fragment.
  * @return string $url The URL resulting from assembling the
  * specified components.
  */
  static function urlunparse($scheme, $host, $port = null, $path = '/',
  $query = '', $fragment = '')
  {
   
  if (!$scheme) {
  $scheme = 'http';
  }
   
  if (!$host) {
  return false;
  }
   
  if (!$path) {
  $path = '';
  }