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: */
13: require_once "Auth/OpenID/Extension.php";
14: require_once "Auth/OpenID/Message.php";
15: require_once "Auth/OpenID/TrustRoot.php";
16:
17: define('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.
22: define('Auth_OpenID_AX_UNLIMITED_VALUES', 'unlimited');
23:
24: // Minimum supported alias length in characters. Here for
25: // completeness.
26: define('Auth_OpenID_AX_MINIMUM_SUPPORTED_ALIAS_LENGTH', 32);
27:
28: /**
29: * AX utility class.
30: *
31: * @package OpenID
32: */
33: class 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 ($thing instanceof 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: */
51: function 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: */
71: class 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: */
84: class 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: */
139: class 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: */
238: function 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: */
268: class 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: * @return 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: */
541: class 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: * 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: */
793: class 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: return null;
941: }
942: return $obj;
943: }
944: }
945:
946: /**
947: * A store request attribute exchange message representation.
948: *
949: * @package OpenID
950: */
951: class Auth_OpenID_AX_StoreRequest extends Auth_OpenID_AX_KeyValueMessage {
952: var $mode = 'store_request';
953:
954: /**
955: * @param array $aliases The namespace aliases to use when making
956: * this store response. Leave as None to use defaults.
957: */
958: function getExtensionArgs($aliases=null)
959: {
960: $ax_args = $this->_newArgs();
961: $kv_args = $this->_getExtensionKVArgs($aliases);
962: Auth_OpenID::update($ax_args, $kv_args);
963: return $ax_args;
964: }
965: }
966:
967: /**
968: * An indication that the store request was processed along with this
969: * OpenID transaction. Use make(), NOT the constructor, to create
970: * response objects.
971: *
972: * @package OpenID
973: */
974: class Auth_OpenID_AX_StoreResponse extends Auth_OpenID_AX_Message {
975: var $SUCCESS_MODE = 'store_response_success';
976: var $FAILURE_MODE = 'store_response_failure';
977:
978: /**
979: * Returns Auth_OpenID_AX_Error on error or an
980: * Auth_OpenID_AX_StoreResponse object on success.
981: */
982: function make($succeeded=true, $error_message=null)
983: {
984: if (($succeeded) && ($error_message !== null)) {
985: return new Auth_OpenID_AX_Error('An error message may only be '.
986: 'included in a failing fetch response');
987: }
988:
989: return new Auth_OpenID_AX_StoreResponse($succeeded, $error_message);
990: }
991:
992: function Auth_OpenID_AX_StoreResponse($succeeded=true, $error_message=null)
993: {
994: if ($succeeded) {
995: $this->mode = $this->SUCCESS_MODE;
996: } else {
997: $this->mode = $this->FAILURE_MODE;
998: }
999:
1000: $this->error_message = $error_message;
1001: }
1002:
1003: /**
1004: * Was this response a success response?
1005: */
1006: function succeeded()
1007: {
1008: return $this->mode == $this->SUCCESS_MODE;
1009: }
1010:
1011: function getExtensionArgs()
1012: {
1013: $ax_args = $this->_newArgs();
1014: if ((!$this->succeeded()) && $this->error_message) {
1015: $ax_args['error'] = $this->error_message;
1016: }
1017:
1018: return $ax_args;
1019: }
1020: }
1021:
1022: