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 static 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 static 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 static 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) && ($ax_args['count.' . $alias] !== Auth_OpenID_AX_UNLIMITED_VALUES)) { 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 static 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