PageRenderTime 271ms CodeModel.GetById 121ms app.highlight 67ms RepoModel.GetById 74ms app.codeStats 0ms

/inc/lib/Auth/OpenID/AX.php

https://bitbucket.org/yoander/mtrack
PHP | 1023 lines | 539 code | 141 blank | 343 comment | 93 complexity | b68edf987cc93f58e24d53e96e84fcbb MD5 | raw file
   1<?php
   2
   3/**
   4 * Implements the OpenID attribute exchange specification, version 1.0
   5 * as of svn revision 370 from openid.net svn.
   6 *
   7 * @package OpenID
   8 */
   9
  10/**
  11 * Require utility classes and functions for the consumer.
  12 */
  13require_once "Auth/OpenID/Extension.php";
  14require_once "Auth/OpenID/Message.php";
  15require_once "Auth/OpenID/TrustRoot.php";
  16
  17define('Auth_OpenID_AX_NS_URI',
  18       'http://openid.net/srv/ax/1.0');
  19
  20// Use this as the 'count' value for an attribute in a FetchRequest to
  21// ask for as many values as the OP can provide.
  22define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited');
  23
  24// Minimum supported alias length in characters.  Here for
  25// completeness.
  26define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32);
  27
  28/**
  29 * AX utility class.
  30 *
  31 * @package OpenID
  32 */
  33class Auth_OpenID_AX {
  34    /**
  35     * @param mixed $thing Any object which may be an
  36     * Auth_OpenID_AX_Error object.
  37     *
  38     * @return bool true if $thing is an Auth_OpenID_AX_Error; false
  39     * if not.
  40     */
  41    function isError($thing)
  42    {
  43        return is_a($thing, 'Auth_OpenID_AX_Error');
  44    }
  45}
  46
  47/**
  48 * Check an alias for invalid characters; raise AXError if any are
  49 * found.  Return None if the alias is valid.
  50 */
  51function Auth_OpenID_AX_checkAlias($alias)
  52{
  53  if (strpos($alias, ',') !== false) {
  54      return new Auth_OpenID_AX_Error(sprintf(
  55                   "Alias %s must not contain comma", $alias));
  56  }
  57  if (strpos($alias, '.') !== false) {
  58      return new Auth_OpenID_AX_Error(sprintf(
  59                   "Alias %s must not contain period", $alias));
  60  }
  61
  62  return true;
  63}
  64
  65/**
  66 * Results from data that does not meet the attribute exchange 1.0
  67 * specification
  68 *
  69 * @package OpenID
  70 */
  71class Auth_OpenID_AX_Error {
  72    function Auth_OpenID_AX_Error($message=null)
  73    {
  74        $this->message = $message;
  75    }
  76}
  77
  78/**
  79 * Abstract class containing common code for attribute exchange
  80 * messages.
  81 *
  82 * @package OpenID
  83 */
  84class Auth_OpenID_AX_Message extends Auth_OpenID_Extension {
  85    /**
  86     * ns_alias: The preferred namespace alias for attribute exchange
  87     * messages
  88     */
  89    var $ns_alias = 'ax';
  90
  91    /**
  92     * mode: The type of this attribute exchange message. This must be
  93     * overridden in subclasses.
  94     */
  95    var $mode = null;
  96
  97    var $ns_uri = Auth_OpenID_AX_NS_URI;
  98
  99    /**
 100     * Return Auth_OpenID_AX_Error if the mode in the attribute
 101     * exchange arguments does not match what is expected for this
 102     * class; true otherwise.
 103     *
 104     * @access private
 105     */
 106    function _checkMode($ax_args)
 107    {
 108        $mode = Auth_OpenID::arrayGet($ax_args, 'mode');
 109        if ($mode != $this->mode) {
 110            return new Auth_OpenID_AX_Error(
 111                            sprintf(
 112                                    "Expected mode '%s'; got '%s'",
 113                                    $this->mode, $mode));
 114        }
 115
 116        return true;
 117    }
 118
 119    /**
 120     * Return a set of attribute exchange arguments containing the
 121     * basic information that must be in every attribute exchange
 122     * message.
 123     *
 124     * @access private
 125     */
 126    function _newArgs()
 127    {
 128        return array('mode' => $this->mode);
 129    }
 130}
 131
 132/**
 133 * Represents a single attribute in an attribute exchange
 134 * request. This should be added to an AXRequest object in order to
 135 * request the attribute.
 136 *
 137 * @package OpenID
 138 */
 139class Auth_OpenID_AX_AttrInfo {
 140    /**
 141     * Construct an attribute information object.  Do not call this
 142     * directly; call make(...) instead.
 143     *
 144     * @param string $type_uri The type URI for this attribute.
 145     *
 146     * @param int $count The number of values of this type to request.
 147     *
 148     * @param bool $required Whether the attribute will be marked as
 149     * required in the request.
 150     *
 151     * @param string $alias The name that should be given to this
 152     * attribute in the request.
 153     */
 154    function Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
 155                                     $alias)
 156    {
 157        /**
 158         * required: Whether the attribute will be marked as required
 159         * when presented to the subject of the attribute exchange
 160         * request.
 161         */
 162        $this->required = $required;
 163
 164        /**
 165         * count: How many values of this type to request from the
 166         * subject. Defaults to one.
 167         */
 168        $this->count = $count;
 169
 170        /**
 171         * type_uri: The identifier that determines what the attribute
 172         * represents and how it is serialized. For example, one type
 173         * URI representing dates could represent a Unix timestamp in
 174         * base 10 and another could represent a human-readable
 175         * string.
 176         */
 177        $this->type_uri = $type_uri;
 178
 179        /**
 180         * alias: The name that should be given to this attribute in
 181         * the request. If it is not supplied, a generic name will be
 182         * assigned. For example, if you want to call a Unix timestamp
 183         * value 'tstamp', set its alias to that value. If two
 184         * attributes in the same message request to use the same
 185         * alias, the request will fail to be generated.
 186         */
 187        $this->alias = $alias;
 188    }
 189
 190    /**
 191     * Construct an attribute information object.  For parameter
 192     * details, see the constructor.
 193     */
 194    function make($type_uri, $count=1, $required=false,
 195                  $alias=null)
 196    {
 197        if ($alias !== null) {
 198            $result = Auth_OpenID_AX_checkAlias($alias);
 199
 200            if (Auth_OpenID_AX::isError($result)) {
 201                return $result;
 202            }
 203        }
 204
 205        return new Auth_OpenID_AX_AttrInfo($type_uri, $count, $required,
 206                                           $alias);
 207    }
 208
 209    /**
 210     * When processing a request for this attribute, the OP should
 211     * call this method to determine whether all available attribute
 212     * values were requested.  If self.count == UNLIMITED_VALUES, this
 213     * returns True.  Otherwise this returns False, in which case
 214     * self.count is an integer.
 215    */
 216    function wantsUnlimitedValues()
 217    {
 218        return $this->count === Auth_OpenID_AX_UNLIMITED_VALUES;
 219    }
 220}
 221
 222/**
 223 * Given a namespace mapping and a string containing a comma-separated
 224 * list of namespace aliases, return a list of type URIs that
 225 * correspond to those aliases.
 226 *
 227 * @param $namespace_map The mapping from namespace URI to alias
 228 * @param $alias_list_s The string containing the comma-separated
 229 * list of aliases. May also be None for convenience.
 230 *
 231 * @return $seq The list of namespace URIs that corresponds to the
 232 * supplied list of aliases. If the string was zero-length or None, an
 233 * empty list will be returned.
 234 *
 235 * return null If an alias is present in the list of aliases but
 236 * is not present in the namespace map.
 237 */
 238function Auth_OpenID_AX_toTypeURIs(&$namespace_map, $alias_list_s)
 239{
 240    $uris = array();
 241
 242    if ($alias_list_s) {
 243        foreach (explode(',', $alias_list_s) as $alias) {
 244            $type_uri = $namespace_map->getNamespaceURI($alias);
 245            if ($type_uri === null) {
 246                // raise KeyError(
 247                // 'No type is defined for attribute name %r' % (alias,))
 248                return new Auth_OpenID_AX_Error(
 249                  sprintf('No type is defined for attribute name %s',
 250                          $alias)
 251                  );
 252            } else {
 253                $uris[] = $type_uri;
 254            }
 255        }
 256    }
 257
 258    return $uris;
 259}
 260
 261/**
 262 * An attribute exchange 'fetch_request' message. This message is sent
 263 * by a relying party when it wishes to obtain attributes about the
 264 * subject of an OpenID authentication request.
 265 *
 266 * @package OpenID
 267 */
 268class Auth_OpenID_AX_FetchRequest extends Auth_OpenID_AX_Message {
 269
 270    var $mode = 'fetch_request';
 271
 272    function Auth_OpenID_AX_FetchRequest($update_url=null)
 273    {
 274        /**
 275         * requested_attributes: The attributes that have been
 276         * requested thus far, indexed by the type URI.
 277         */
 278        $this->requested_attributes = array();
 279
 280        /**
 281         * update_url: A URL that will accept responses for this
 282         * attribute exchange request, even in the absence of the user
 283         * who made this request.
 284        */
 285        $this->update_url = $update_url;
 286    }
 287
 288    /**
 289     * Add an attribute to this attribute exchange request.
 290     *
 291     * @param attribute: The attribute that is being requested
 292     * @return true on success, false when the requested attribute is
 293     * already present in this fetch request.
 294     */
 295    function add($attribute)
 296    {
 297        if ($this->contains($attribute->type_uri)) {
 298            return new Auth_OpenID_AX_Error(
 299              sprintf("The attribute %s has already been requested",
 300                      $attribute->type_uri));
 301        }
 302
 303        $this->requested_attributes[$attribute->type_uri] = $attribute;
 304
 305        return true;
 306    }
 307
 308    /**
 309     * Get the serialized form of this attribute fetch request.
 310     *
 311     * @returns Auth_OpenID_AX_FetchRequest The fetch request message parameters
 312     */
 313    function getExtensionArgs()
 314    {
 315        $aliases = new Auth_OpenID_NamespaceMap();
 316
 317        $required = array();
 318        $if_available = array();
 319
 320        $ax_args = $this->_newArgs();
 321
 322        foreach ($this->requested_attributes as $type_uri => $attribute) {
 323            if ($attribute->alias === null) {
 324                $alias = $aliases->add($type_uri);
 325            } else {
 326                $alias = $aliases->addAlias($type_uri, $attribute->alias);
 327
 328                if ($alias === null) {
 329                    return new Auth_OpenID_AX_Error(
 330                      sprintf("Could not add alias %s for URI %s",
 331                              $attribute->alias, $type_uri
 332                      ));
 333                }
 334            }
 335
 336            if ($attribute->required) {
 337                $required[] = $alias;
 338            } else {
 339                $if_available[] = $alias;
 340            }
 341
 342            if ($attribute->count != 1) {
 343                $ax_args['count.' . $alias] = strval($attribute->count);
 344            }
 345
 346            $ax_args['type.' . $alias] = $type_uri;
 347        }
 348
 349        if ($required) {
 350            $ax_args['required'] = implode(',', $required);
 351        }
 352
 353        if ($if_available) {
 354            $ax_args['if_available'] = implode(',', $if_available);
 355        }
 356
 357        return $ax_args;
 358    }
 359
 360    /**
 361     * Get the type URIs for all attributes that have been marked as
 362     * required.
 363     *
 364     * @return A list of the type URIs for attributes that have been
 365     * marked as required.
 366     */
 367    function getRequiredAttrs()
 368    {
 369        $required = array();
 370        foreach ($this->requested_attributes as $type_uri => $attribute) {
 371            if ($attribute->required) {
 372                $required[] = $type_uri;
 373            }
 374        }
 375
 376        return $required;
 377    }
 378
 379    /**
 380     * Extract a FetchRequest from an OpenID message
 381     *
 382     * @param request: The OpenID request containing the attribute
 383     * fetch request
 384     *
 385     * @returns mixed An Auth_OpenID_AX_Error or the
 386     * Auth_OpenID_AX_FetchRequest extracted from the request message if
 387     * successful
 388     */
 389    function &fromOpenIDRequest($request)
 390    {
 391        $m = $request->message;
 392        $obj = new Auth_OpenID_AX_FetchRequest();
 393        $ax_args = $m->getArgs($obj->ns_uri);
 394
 395        $result = $obj->parseExtensionArgs($ax_args);
 396
 397        if (Auth_OpenID_AX::isError($result)) {
 398            return $result;
 399        }
 400
 401        if ($obj->update_url) {
 402            // Update URL must match the openid.realm of the
 403            // underlying OpenID 2 message.
 404            $realm = $m->getArg(Auth_OpenID_OPENID_NS, 'realm',
 405                        $m->getArg(
 406                                  Auth_OpenID_OPENID_NS,
 407                                  'return_to'));
 408
 409            if (!$realm) {
 410                $obj = new Auth_OpenID_AX_Error(
 411                  sprintf("Cannot validate update_url %s " .
 412                          "against absent realm", $obj->update_url));
 413            } else if (!Auth_OpenID_TrustRoot::match($realm,
 414                                                     $obj->update_url)) {
 415                $obj = new Auth_OpenID_AX_Error(
 416                  sprintf("Update URL %s failed validation against realm %s",
 417                          $obj->update_url, $realm));
 418            }
 419        }
 420
 421        return $obj;
 422    }
 423
 424    /**
 425     * Given attribute exchange arguments, populate this FetchRequest.
 426     *
 427     * @return $result Auth_OpenID_AX_Error if the data to be parsed
 428     * does not follow the attribute exchange specification. At least
 429     * when 'if_available' or 'required' is not specified for a
 430     * particular attribute type.  Returns true otherwise.
 431    */
 432    function parseExtensionArgs($ax_args)
 433    {
 434        $result = $this->_checkMode($ax_args);
 435        if (Auth_OpenID_AX::isError($result)) {
 436            return $result;
 437        }
 438
 439        $aliases = new Auth_OpenID_NamespaceMap();
 440
 441        foreach ($ax_args as $key => $value) {
 442            if (strpos($key, 'type.') === 0) {
 443                $alias = substr($key, 5);
 444                $type_uri = $value;
 445
 446                $alias = $aliases->addAlias($type_uri, $alias);
 447
 448                if ($alias === null) {
 449                    return new Auth_OpenID_AX_Error(
 450                      sprintf("Could not add alias %s for URI %s",
 451                              $alias, $type_uri)
 452                      );
 453                }
 454
 455                $count_s = Auth_OpenID::arrayGet($ax_args, 'count.' . $alias);
 456                if ($count_s) {
 457                    $count = Auth_OpenID::intval($count_s);
 458                    if (($count === false) &&
 459                        ($count_s === Auth_OpenID_AX_UNLIMITED_VALUES)) {
 460                        $count = $count_s;
 461                    }
 462                } else {
 463                    $count = 1;
 464                }
 465
 466                if ($count === false) {
 467                    return new Auth_OpenID_AX_Error(
 468                      sprintf("Integer value expected for %s, got %s",
 469                              'count.' . $alias, $count_s));
 470                }
 471
 472                $attrinfo = Auth_OpenID_AX_AttrInfo::make($type_uri, $count,
 473                                                          false, $alias);
 474
 475                if (Auth_OpenID_AX::isError($attrinfo)) {
 476                    return $attrinfo;
 477                }
 478
 479                $this->add($attrinfo);
 480            }
 481        }
 482
 483        $required = Auth_OpenID_AX_toTypeURIs($aliases,
 484                         Auth_OpenID::arrayGet($ax_args, 'required'));
 485
 486        foreach ($required as $type_uri) {
 487            $attrib =& $this->requested_attributes[$type_uri];
 488            $attrib->required = true;
 489        }
 490
 491        $if_available = Auth_OpenID_AX_toTypeURIs($aliases,
 492                             Auth_OpenID::arrayGet($ax_args, 'if_available'));
 493
 494        $all_type_uris = array_merge($required, $if_available);
 495
 496        foreach ($aliases->iterNamespaceURIs() as $type_uri) {
 497            if (!in_array($type_uri, $all_type_uris)) {
 498                return new Auth_OpenID_AX_Error(
 499                  sprintf('Type URI %s was in the request but not ' .
 500                          'present in "required" or "if_available"',
 501                          $type_uri));
 502
 503            }
 504        }
 505
 506        $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
 507
 508        return true;
 509    }
 510
 511    /**
 512     * Iterate over the AttrInfo objects that are contained in this
 513     * fetch_request.
 514     */
 515    function iterAttrs()
 516    {
 517        return array_values($this->requested_attributes);
 518    }
 519
 520    function iterTypes()
 521    {
 522        return array_keys($this->requested_attributes);
 523    }
 524
 525    /**
 526     * Is the given type URI present in this fetch_request?
 527     */
 528    function contains($type_uri)
 529    {
 530        return in_array($type_uri, $this->iterTypes());
 531    }
 532}
 533
 534/**
 535 * An abstract class that implements a message that has attribute keys
 536 * and values. It contains the common code between fetch_response and
 537 * store_request.
 538 *
 539 * @package OpenID
 540 */
 541class Auth_OpenID_AX_KeyValueMessage extends Auth_OpenID_AX_Message {
 542
 543    function Auth_OpenID_AX_KeyValueMessage()
 544    {
 545        $this->data = array();
 546    }
 547
 548    /**
 549     * Add a single value for the given attribute type to the
 550     * message. If there are already values specified for this type,
 551     * this value will be sent in addition to the values already
 552     * specified.
 553     *
 554     * @param type_uri: The URI for the attribute
 555     * @param value: The value to add to the response to the relying
 556     * party for this attribute
 557     * @return null
 558     */
 559    function addValue($type_uri, $value)
 560    {
 561        if (!array_key_exists($type_uri, $this->data)) {
 562            $this->data[$type_uri] = array();
 563        }
 564
 565        $values =& $this->data[$type_uri];
 566        $values[] = $value;
 567    }
 568
 569    /**
 570     * Set the values for the given attribute type. This replaces any
 571     * values that have already been set for this attribute.
 572     *
 573     * @param type_uri: The URI for the attribute
 574     * @param values: A list of values to send for this attribute.
 575     */
 576    function setValues($type_uri, &$values)
 577    {
 578        $this->data[$type_uri] =& $values;
 579    }
 580
 581    /**
 582     * Get the extension arguments for the key/value pairs contained
 583     * in this message.
 584     *
 585     * @param aliases: An alias mapping. Set to None if you don't care
 586     * about the aliases for this request.
 587     *
 588     * @access private
 589     */
 590    function _getExtensionKVArgs(&$aliases)
 591    {
 592        if ($aliases === null) {
 593            $aliases = new Auth_OpenID_NamespaceMap();
 594        }
 595
 596        $ax_args = array();
 597
 598        foreach ($this->data as $type_uri => $values) {
 599            $alias = $aliases->add($type_uri);
 600
 601            $ax_args['type.' . $alias] = $type_uri;
 602            $ax_args['count.' . $alias] = strval(count($values));
 603
 604            foreach ($values as $i => $value) {
 605              $key = sprintf('value.%s.%d', $alias, $i + 1);
 606              $ax_args[$key] = $value;
 607            }
 608        }
 609
 610        return $ax_args;
 611    }
 612
 613    /**
 614     * Parse attribute exchange key/value arguments into this object.
 615     *
 616     * @param ax_args: The attribute exchange fetch_response
 617     * arguments, with namespacing removed.
 618     *
 619     * @return Auth_OpenID_AX_Error or true
 620     */
 621    function parseExtensionArgs($ax_args)
 622    {
 623        $result = $this->_checkMode($ax_args);
 624        if (Auth_OpenID_AX::isError($result)) {
 625            return $result;
 626        }
 627
 628        $aliases = new Auth_OpenID_NamespaceMap();
 629
 630        foreach ($ax_args as $key => $value) {
 631            if (strpos($key, 'type.') === 0) {
 632                $type_uri = $value;
 633                $alias = substr($key, 5);
 634
 635                $result = Auth_OpenID_AX_checkAlias($alias);
 636
 637                if (Auth_OpenID_AX::isError($result)) {
 638                    return $result;
 639                }
 640
 641                $alias = $aliases->addAlias($type_uri, $alias);
 642
 643                if ($alias === null) {
 644                    return new Auth_OpenID_AX_Error(
 645                      sprintf("Could not add alias %s for URI %s",
 646                              $alias, $type_uri)
 647                      );
 648                }
 649            }
 650        }
 651
 652        foreach ($aliases->iteritems() as $pair) {
 653            list($type_uri, $alias) = $pair;
 654
 655            if (array_key_exists('count.' . $alias, $ax_args)) {
 656
 657                $count_key = 'count.' . $alias;
 658                $count_s = $ax_args[$count_key];
 659
 660                $count = Auth_OpenID::intval($count_s);
 661
 662                if ($count === false) {
 663                    return new Auth_OpenID_AX_Error(
 664                      sprintf("Integer value expected for %s, got %s",
 665                              'count. %s' . $alias, $count_s,
 666                              Auth_OpenID_AX_UNLIMITED_VALUES)
 667                                                    );
 668                }
 669
 670                $values = array();
 671                for ($i = 1; $i < $count + 1; $i++) {
 672                    $value_key = sprintf('value.%s.%d', $alias, $i);
 673
 674                    if (!array_key_exists($value_key, $ax_args)) {
 675                      return new Auth_OpenID_AX_Error(
 676                        sprintf(
 677                                "No value found for key %s",
 678                                $value_key));
 679                    }
 680
 681                    $value = $ax_args[$value_key];
 682                    $values[] = $value;
 683                }
 684            } else {
 685                $key = 'value.' . $alias;
 686
 687                if (!array_key_exists($key, $ax_args)) {
 688                  return new Auth_OpenID_AX_Error(
 689                    sprintf(
 690                            "No value found for key %s",
 691                            $key));
 692                }
 693
 694                $value = $ax_args['value.' . $alias];
 695
 696                if ($value == '') {
 697                    $values = array();
 698                } else {
 699                    $values = array($value);
 700                }
 701            }
 702
 703            $this->data[$type_uri] = $values;
 704        }
 705
 706        return true;
 707    }
 708
 709    /**
 710     * Get a single value for an attribute. If no value was sent for
 711     * this attribute, use the supplied default. If there is more than
 712     * one value for this attribute, this method will fail.
 713     *
 714     * @param type_uri: The URI for the attribute
 715     * @param default: The value to return if the attribute was not
 716     * sent in the fetch_response.
 717     *
 718     * @return $value Auth_OpenID_AX_Error on failure or the value of
 719     * the attribute in the fetch_response message, or the default
 720     * supplied
 721     */
 722    function getSingle($type_uri, $default=null)
 723    {
 724        $values = Auth_OpenID::arrayGet($this->data, $type_uri);
 725        if (!$values) {
 726            return $default;
 727        } else if (count($values) == 1) {
 728            return $values[0];
 729        } else {
 730            return new Auth_OpenID_AX_Error(
 731              sprintf('More than one value present for %s',
 732                      $type_uri)
 733              );
 734        }
 735    }
 736
 737    /**
 738     * Get the list of values for this attribute in the
 739     * fetch_response.
 740     *
 741     * XXX: what to do if the values are not present? default
 742     * parameter? this is funny because it's always supposed to return
 743     * a list, so the default may break that, though it's provided by
 744     * the user's code, so it might be okay. If no default is
 745     * supplied, should the return be None or []?
 746     *
 747     * @param type_uri: The URI of the attribute
 748     *
 749     * @return $values The list of values for this attribute in the
 750     * response. May be an empty list.  If the attribute was not sent
 751     * in the response, returns Auth_OpenID_AX_Error.
 752     */
 753    function get($type_uri)
 754    {
 755        if (array_key_exists($type_uri, $this->data)) {
 756            return $this->data[$type_uri];
 757        } else {
 758            return new Auth_OpenID_AX_Error(
 759              sprintf("Type URI %s not found in response",
 760                      $type_uri)
 761              );
 762        }
 763    }
 764
 765    /**
 766     * Get the number of responses for a particular attribute in this
 767     * fetch_response message.
 768     *
 769     * @param type_uri: The URI of the attribute
 770     *
 771     * @returns int The number of values sent for this attribute.  If
 772     * the attribute was not sent in the response, returns
 773     * Auth_OpenID_AX_Error.
 774     */
 775    function count($type_uri)
 776    {
 777        if (array_key_exists($type_uri, $this->data)) {
 778            return count($this->get($type_uri));
 779        } else {
 780            return new Auth_OpenID_AX_Error(
 781              sprintf("Type URI %s not found in response",
 782                      $type_uri)
 783              );
 784        }
 785    }
 786}
 787
 788/**
 789 * A fetch_response attribute exchange message.
 790 *
 791 * @package OpenID
 792 */
 793class Auth_OpenID_AX_FetchResponse extends Auth_OpenID_AX_KeyValueMessage {
 794    var $mode = 'fetch_response';
 795
 796    function Auth_OpenID_AX_FetchResponse($update_url=null)
 797    {
 798        $this->Auth_OpenID_AX_KeyValueMessage();
 799        $this->update_url = $update_url;
 800    }
 801
 802    /**
 803     * Serialize this object into arguments in the attribute exchange
 804     * namespace
 805     *
 806     * @return $args The dictionary of unqualified attribute exchange
 807     * arguments that represent this fetch_response, or
 808     * Auth_OpenID_AX_Error on error.
 809     */
 810    function getExtensionArgs($request=null)
 811    {
 812        $aliases = new Auth_OpenID_NamespaceMap();
 813
 814        $zero_value_types = array();
 815
 816        if ($request !== null) {
 817            // Validate the data in the context of the request (the
 818            // same attributes should be present in each, and the
 819            // counts in the response must be no more than the counts
 820            // in the request)
 821
 822            foreach ($this->data as $type_uri => $unused) {
 823                if (!$request->contains($type_uri)) {
 824                    return new Auth_OpenID_AX_Error(
 825                      sprintf("Response attribute not present in request: %s",
 826                              $type_uri)
 827                      );
 828                }
 829            }
 830
 831            foreach ($request->iterAttrs() as $attr_info) {
 832                // Copy the aliases from the request so that reading
 833                // the response in light of the request is easier
 834                if ($attr_info->alias === null) {
 835                    $aliases->add($attr_info->type_uri);
 836                } else {
 837                    $alias = $aliases->addAlias($attr_info->type_uri,
 838                                                $attr_info->alias);
 839
 840                    if ($alias === null) {
 841                        return new Auth_OpenID_AX_Error(
 842                          sprintf("Could not add alias %s for URI %s",
 843                                  $attr_info->alias, $attr_info->type_uri)
 844                          );
 845                    }
 846                }
 847
 848                if (array_key_exists($attr_info->type_uri, $this->data)) {
 849                    $values = $this->data[$attr_info->type_uri];
 850                } else {
 851                    $values = array();
 852                    $zero_value_types[] = $attr_info;
 853                }
 854
 855                if (($attr_info->count != Auth_OpenID_AX_UNLIMITED_VALUES) &&
 856                    ($attr_info->count < count($values))) {
 857                    return new Auth_OpenID_AX_Error(
 858                      sprintf("More than the number of requested values " .
 859                              "were specified for %s",
 860                              $attr_info->type_uri)
 861                      );
 862                }
 863            }
 864        }
 865
 866        $kv_args = $this->_getExtensionKVArgs($aliases);
 867
 868        // Add the KV args into the response with the args that are
 869        // unique to the fetch_response
 870        $ax_args = $this->_newArgs();
 871
 872        // For each requested attribute, put its type/alias and count
 873        // into the response even if no data were returned.
 874        foreach ($zero_value_types as $attr_info) {
 875            $alias = $aliases->getAlias($attr_info->type_uri);
 876            $kv_args['type.' . $alias] = $attr_info->type_uri;
 877            $kv_args['count.' . $alias] = '0';
 878        }
 879
 880        $update_url = null;
 881        if ($request) {
 882            $update_url = $request->update_url;
 883        } else {
 884            $update_url = $this->update_url;
 885        }
 886
 887        if ($update_url) {
 888            $ax_args['update_url'] = $update_url;
 889        }
 890
 891        Auth_OpenID::update(&$ax_args, $kv_args);
 892
 893        return $ax_args;
 894    }
 895
 896    /**
 897     * @return $result Auth_OpenID_AX_Error on failure or true on
 898     * success.
 899     */
 900    function parseExtensionArgs($ax_args)
 901    {
 902        $result = parent::parseExtensionArgs($ax_args);
 903
 904        if (Auth_OpenID_AX::isError($result)) {
 905            return $result;
 906        }
 907
 908        $this->update_url = Auth_OpenID::arrayGet($ax_args, 'update_url');
 909
 910        return true;
 911    }
 912
 913    /**
 914     * Construct a FetchResponse object from an OpenID library
 915     * SuccessResponse object.
 916     *
 917     * @param success_response: A successful id_res response object
 918     *
 919     * @param signed: Whether non-signed args should be processsed. If
 920     * True (the default), only signed arguments will be processsed.
 921     *
 922     * @return $response A FetchResponse containing the data from the
 923     * OpenID message
 924     */
 925    function fromSuccessResponse($success_response, $signed=true)
 926    {
 927        $obj = new Auth_OpenID_AX_FetchResponse();
 928        if ($signed) {
 929            $ax_args = $success_response->getSignedNS($obj->ns_uri);
 930        } else {
 931            $ax_args = $success_response->message->getArgs($obj->ns_uri);
 932        }
 933        if ($ax_args === null || Auth_OpenID::isFailure($ax_args) ||
 934              sizeof($ax_args) == 0) {
 935            return null;
 936        }
 937
 938        $result = $obj->parseExtensionArgs($ax_args);
 939        if (Auth_OpenID_AX::isError($result)) {
 940            #XXX log me
 941            return null;
 942        }
 943        return $obj;
 944    }
 945}
 946
 947/**
 948 * A store request attribute exchange message representation.
 949 *
 950 * @package OpenID
 951 */
 952class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage {
 953    var $mode = 'store_request';
 954
 955    /**
 956     * @param array $aliases The namespace aliases to use when making
 957     * this store response. Leave as None to use defaults.
 958     */
 959    function getExtensionArgs($aliases=null)
 960    {
 961        $ax_args = $this->_newArgs();
 962        $kv_args = $this->_getExtensionKVArgs($aliases);
 963        Auth_OpenID::update(&$ax_args, $kv_args);
 964        return $ax_args;
 965    }
 966}
 967
 968/**
 969 * An indication that the store request was processed along with this
 970 * OpenID transaction.  Use make(), NOT the constructor, to create
 971 * response objects.
 972 *
 973 * @package OpenID
 974 */
 975class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message {
 976    var $SUCCESS_MODE = 'store_response_success';
 977    var $FAILURE_MODE = 'store_response_failure';
 978
 979    /**
 980     * Returns Auth_OpenID_AX_Error on error or an
 981     * Auth_OpenID_AX_StoreResponse object on success.
 982     */
 983    function &make($succeeded=true, $error_message=null)
 984    {
 985        if (($succeeded) && ($error_message !== null)) {
 986            return new Auth_OpenID_AX_Error('An error message may only be '.
 987                                    'included in a failing fetch response');
 988        }
 989
 990        return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message);
 991    }
 992
 993    function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null)
 994    {
 995        if ($succeeded) {
 996            $this->mode = $this->SUCCESS_MODE;
 997        } else {
 998            $this->mode = $this->FAILURE_MODE;
 999        }
1000
1001        $this->error_message = $error_message;
1002    }
1003
1004    /**
1005     * Was this response a success response?
1006     */
1007    function succeeded()
1008    {
1009        return $this->mode == $this->SUCCESS_MODE;
1010    }
1011
1012    function getExtensionArgs()
1013    {
1014        $ax_args = $this->_newArgs();
1015        if ((!$this->succeeded()) && $this->error_message) {
1016            $ax_args['error'] = $this->error_message;
1017        }
1018
1019        return $ax_args;
1020    }
1021}
1022
1023?>