import
[web.mtrack] / inc / lib / Auth / OpenID / Message.php
1 <?php
2
3 /**
4  * Extension argument processing code
5  *
6  * @package OpenID
7  */
8
9 /**
10  * Import tools needed to deal with messages.
11  */
12 require_once 'Auth/OpenID.php';
13 require_once 'Auth/OpenID/KVForm.php';
14 require_once 'Auth/Yadis/XML.php';
15 require_once 'Auth/OpenID/Consumer.php'; // For Auth_OpenID_FailureResponse
16
17 // This doesn't REALLY belong here, but where is better?
18 define('Auth_OpenID_IDENTIFIER_SELECT',
19        "http://specs.openid.net/auth/2.0/identifier_select");
20
21 // URI for Simple Registration extension, the only commonly deployed
22 // OpenID 1.x extension, and so a special case
23 define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
24
25 // The OpenID 1.X namespace URI
26 define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
27 define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
28
29 function Auth_OpenID_isOpenID1($ns)
30 {
31     return ($ns == Auth_OpenID_THE_OTHER_OPENID1_NS) ||
32         ($ns == Auth_OpenID_OPENID1_NS);
33 }
34
35 // The OpenID 2.0 namespace URI
36 define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
37
38 // The namespace consisting of pairs with keys that are prefixed with
39 // "openid."  but not in another namespace.
40 define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
41
42 // The null namespace, when it is an allowed OpenID namespace
43 define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
44
45 // The top-level namespace, excluding all pairs with keys that start
46 // with "openid."
47 define('Auth_OpenID_BARE_NS', 'Bare namespace');
48
49 // Sentinel for Message implementation to indicate that getArg should
50 // return null instead of returning a default.
51 define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
52
53 // Limit, in bytes, of identity provider and return_to URLs, including
54 // response payload.  See OpenID 1.1 specification, Appendix D.
55 define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
56
57 // All OpenID protocol fields.  Used to check namespace aliases.
58 global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
59 $Auth_OpenID_OPENID_PROTOCOL_FIELDS = array(
60     'ns', 'mode', 'error', 'return_to', 'contact', 'reference',
61     'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen',
62     'dh_consumer_public', 'claimed_id', 'identity', 'realm',
63     'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig',
64     'assoc_handle', 'trust_root', 'openid');
65
66 // Global namespace / alias registration map.  See
67 // Auth_OpenID_registerNamespaceAlias.
68 global $Auth_OpenID_registered_aliases;
69 $Auth_OpenID_registered_aliases = array();
70
71 /**
72  * Registers a (namespace URI, alias) mapping in a global namespace
73  * alias map.  Raises NamespaceAliasRegistrationError if either the
74  * namespace URI or alias has already been registered with a different
75  * value.  This function is required if you want to use a namespace
76  * with an OpenID 1 message.
77  */
78 function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
79 {
80     global $Auth_OpenID_registered_aliases;
81
82     if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
83                               $alias) == $namespace_uri) {
84         return true;
85     }
86
87     if (in_array($namespace_uri,
88                  array_values($Auth_OpenID_registered_aliases))) {
89         return false;
90     }
91
92     if (in_array($alias, array_keys($Auth_OpenID_registered_aliases))) {
93         return false;
94     }
95
96     $Auth_OpenID_registered_aliases[$alias] = $namespace_uri;
97     return true;
98 }
99
100 /**
101  * Removes a (namespace_uri, alias) registration from the global
102  * namespace alias map.  Returns true if the removal succeeded; false
103  * if not (if the mapping did not exist).
104  */
105 function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
106 {
107     global $Auth_OpenID_registered_aliases;
108
109     if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
110                               $alias) === $namespace_uri) {
111         unset($Auth_OpenID_registered_aliases[$alias]);
112         return true;
113     }
114
115     return false;
116 }
117
118 /**
119  * An Auth_OpenID_Mapping maintains a mapping from arbitrary keys to
120  * arbitrary values.  (This is unlike an ordinary PHP array, whose
121  * keys may be only simple scalars.)
122  *
123  * @package OpenID
124  */
125 class Auth_OpenID_Mapping {
126     /**
127      * Initialize a mapping.  If $classic_array is specified, its keys
128      * and values are used to populate the mapping.
129      */
130     function Auth_OpenID_Mapping($classic_array = null)
131     {
132         $this->keys = array();
133         $this->values = array();
134
135         if (is_array($classic_array)) {
136             foreach ($classic_array as $key => $value) {
137                 $this->set($key, $value);
138             }
139         }
140     }
141
142     /**
143      * Returns true if $thing is an Auth_OpenID_Mapping object; false
144      * if not.
145      */
146     function isA($thing)
147     {
148         return (is_object($thing) &&
149                 strtolower(get_class($thing)) == 'auth_openid_mapping');
150     }
151
152     /**
153      * Returns an array of the keys in the mapping.
154      */
155     function keys()
156     {
157         return $this->keys;
158     }
159
160     /**
161      * Returns an array of values in the mapping.
162      */
163     function values()
164     {
165         return $this->values;
166     }
167
168     /**
169      * Returns an array of (key, value) pairs in the mapping.
170      */
171     function items()
172     {
173         $temp = array();
174
175         for ($i = 0; $i < count($this->keys); $i++) {
176             $temp[] = array($this->keys[$i],
177                             $this->values[$i]);
178         }
179         return $temp;
180     }
181
182     /**
183      * Returns the "length" of the mapping, or the number of keys.
184      */
185     function len()
186     {
187         return count($this->keys);
188     }
189
190     /**
191      * Sets a key-value pair in the mapping.  If the key already
192      * exists, its value is replaced with the new value.
193      */
194     function set($key, $value)
195     {
196         $index = array_search($key, $this->keys);
197
198         if ($index !== false) {
199             $this->values[$index] = $value;
200         } else {
201             $this->keys[] = $key;
202             $this->values[] = $value;
203         }
204     }
205
206     /**
207      * Gets a specified value from the mapping, associated with the
208      * specified key.  If the key does not exist in the mapping,
209      * $default is returned instead.
210      */
211     function get($key, $default = null)
212     {
213         $index = array_search($key, $this->keys);
214
215         if ($index !== false) {
216             return $this->values[$index];
217         } else {
218             return $default;
219         }
220     }
221
222     /**
223      * @access private
224      */
225     function _reflow()
226     {
227         // PHP is broken yet again.  Sort the arrays to remove the
228         // hole in the numeric indexes that make up the array.
229         $old_keys = $this->keys;
230         $old_values = $this->values;
231
232         $this->keys = array();
233         $this->values = array();
234
235         foreach ($old_keys as $k) {
236             $this->keys[] = $k;
237         }
238
239         foreach ($old_values as $v) {
240             $this->values[] = $v;
241         }
242     }
243
244     /**
245      * Deletes a key-value pair from the mapping with the specified
246      * key.
247      */
248     function del($key)
249     {
250         $index = array_search($key, $this->keys);
251
252         if ($index !== false) {
253             unset($this->keys[$index]);
254             unset($this->values[$index]);
255             $this->_reflow();
256             return true;
257         }
258         return false;
259     }
260
261     /**
262      * Returns true if the specified value has a key in the mapping;
263      * false if not.
264      */
265     function contains($value)
266     {
267         return (array_search($value, $this->keys) !== false);
268     }
269 }
270
271 /**
272  * Maintains a bijective map between namespace uris and aliases.
273  *
274  * @package OpenID
275  */
276 class Auth_OpenID_NamespaceMap {
277     function Auth_OpenID_NamespaceMap()
278     {
279         $this->alias_to_namespace = new Auth_OpenID_Mapping();
280         $this->namespace_to_alias = new Auth_OpenID_Mapping();
281         $this->implicit_namespaces = array();
282     }
283
284     function getAlias($namespace_uri)
285     {
286         return $this->namespace_to_alias->get($namespace_uri);
287     }
288
289     function getNamespaceURI($alias)
290     {
291         return $this->alias_to_namespace->get($alias);
292     }
293
294     function iterNamespaceURIs()
295     {
296         // Return an iterator over the namespace URIs
297         return $this->namespace_to_alias->keys();
298     }
299
300     function iterAliases()
301     {
302         // Return an iterator over the aliases"""
303         return $this->alias_to_namespace->keys();
304     }
305
306     function iteritems()
307     {
308         return $this->namespace_to_alias->items();
309     }
310
311     function isImplicit($namespace_uri)
312     {
313         return in_array($namespace_uri, $this->implicit_namespaces);
314     }
315
316     function addAlias($namespace_uri, $desired_alias, $implicit=false)
317     {
318         // Add an alias from this namespace URI to the desired alias
319         global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
320
321         // Check that desired_alias is not an openid protocol field as
322         // per the spec.
323         if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) {
324             Auth_OpenID::log("\"%s\" is not an allowed namespace alias",
325                             $desired_alias);
326             return null;
327         }
328
329         // Check that desired_alias does not contain a period as per
330         // the spec.
331         if (strpos($desired_alias, '.') !== false) {
332             Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
333             return null;
334         }
335
336         // Check that there is not a namespace already defined for the
337         // desired alias
338         $current_namespace_uri =
339             $this->alias_to_namespace->get($desired_alias);
340
341         if (($current_namespace_uri !== null) &&
342             ($current_namespace_uri != $namespace_uri)) {
343             Auth_OpenID::log('Cannot map "%s" because previous mapping exists',
344                             $namespace_uri);
345             return null;
346         }
347
348         // Check that there is not already a (different) alias for
349         // this namespace URI
350         $alias = $this->namespace_to_alias->get($namespace_uri);
351
352         if (($alias !== null) && ($alias != $desired_alias)) {
353             Auth_OpenID::log('Cannot map %s to alias %s. ' .
354                             'It is already mapped to alias %s',
355                             $namespace_uri, $desired_alias, $alias);
356             return null;
357         }
358
359         assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) ||
360                is_string($desired_alias));
361
362         $this->alias_to_namespace->set($desired_alias, $namespace_uri);
363         $this->namespace_to_alias->set($namespace_uri, $desired_alias);
364         if ($implicit) {
365             array_push($this->implicit_namespaces, $namespace_uri);
366         }
367
368         return $desired_alias;
369     }
370
371     function add($namespace_uri)
372     {
373         // Add this namespace URI to the mapping, without caring what
374         // alias it ends up with
375
376         // See if this namespace is already mapped to an alias
377         $alias = $this->namespace_to_alias->get($namespace_uri);
378
379         if ($alias !== null) {
380             return $alias;
381         }
382
383         // Fall back to generating a numerical alias
384         $i = 0;
385         while (1) {
386             $alias = 'ext' . strval($i);
387             if ($this->addAlias($namespace_uri, $alias) === null) {
388                 $i += 1;
389             } else {
390                 return $alias;
391             }
392         }
393
394         // Should NEVER be reached!
395         return null;
396     }
397
398     function contains($namespace_uri)
399     {
400         return $this->isDefined($namespace_uri);
401     }
402
403     function isDefined($namespace_uri)
404     {
405         return $this->namespace_to_alias->contains($namespace_uri);
406     }
407 }
408
409 /**
410  * In the implementation of this object, null represents the global
411  * namespace as well as a namespace with no key.
412  *
413  * @package OpenID
414  */
415 class Auth_OpenID_Message {
416
417     function Auth_OpenID_Message($openid_namespace = null)
418     {
419         // Create an empty Message
420         $this->allowed_openid_namespaces = array(
421                                Auth_OpenID_OPENID1_NS,
422                                Auth_OpenID_THE_OTHER_OPENID1_NS,
423                                Auth_OpenID_OPENID2_NS);
424
425         $this->args = new Auth_OpenID_Mapping();
426         $this->namespaces = new Auth_OpenID_NamespaceMap();
427         if ($openid_namespace === null) {
428             $this->_openid_ns_uri = null;
429         } else {
430             $implicit = Auth_OpenID_isOpenID1($openid_namespace);
431             $this->setOpenIDNamespace($openid_namespace, $implicit);
432         }
433     }
434
435     function isOpenID1()
436     {
437         return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
438     }
439
440     function isOpenID2()
441     {
442         return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
443     }
444
445     function fromPostArgs($args)
446     {
447         // Construct a Message containing a set of POST arguments
448         $obj = new Auth_OpenID_Message();
449
450         // Partition into "openid." args and bare args
451         $openid_args = array();
452         foreach ($args as $key => $value) {
453
454             if (is_array($value)) {
455                 return null;
456             }
457
458             $parts = explode('.', $key, 2);
459
460             if (count($parts) == 2) {
461                 list($prefix, $rest) = $parts;
462             } else {
463                 $prefix = null;
464             }
465
466             if ($prefix != 'openid') {
467                 $obj->args->set(array(Auth_OpenID_BARE_NS, $key), $value);
468             } else {
469                 $openid_args[$rest] = $value;
470             }
471         }
472
473         if ($obj->_fromOpenIDArgs($openid_args)) {
474             return $obj;
475         } else {
476             return null;
477         }
478     }
479
480     function fromOpenIDArgs($openid_args)
481     {
482         // Takes an array.
483
484         // Construct a Message from a parsed KVForm message
485         $obj = new Auth_OpenID_Message();
486         if ($obj->_fromOpenIDArgs($openid_args)) {
487             return $obj;
488         } else {
489             return null;
490         }
491     }
492
493     /**
494      * @access private
495      */
496     function _fromOpenIDArgs($openid_args)
497     {
498         global $Auth_OpenID_registered_aliases;
499
500         // Takes an Auth_OpenID_Mapping instance OR an array.
501
502         if (!Auth_OpenID_Mapping::isA($openid_args)) {
503             $openid_args = new Auth_OpenID_Mapping($openid_args);
504         }
505
506         $ns_args = array();
507
508         // Resolve namespaces
509         foreach ($openid_args->items() as $pair) {
510             list($rest, $value) = $pair;
511
512             $parts = explode('.', $rest, 2);
513
514             if (count($parts) == 2) {
515                 list($ns_alias, $ns_key) = $parts;
516             } else {
517                 $ns_alias = Auth_OpenID_NULL_NAMESPACE;
518                 $ns_key = $rest;
519             }
520
521             if ($ns_alias == 'ns') {
522                 if ($this->namespaces->addAlias($value, $ns_key) === null) {
523                     return false;
524                 }
525             } else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) &&
526                        ($ns_key == 'ns')) {
527                 // null namespace
528                 if ($this->setOpenIDNamespace($value, false) === false) {
529                     return false;
530                 }
531             } else {
532                 $ns_args[] = array($ns_alias, $ns_key, $value);
533             }
534         }
535
536         if (!$this->getOpenIDNamespace()) {
537             if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) ===
538                 false) {
539                 return false;
540             }
541         }
542
543         // Actually put the pairs into the appropriate namespaces
544         foreach ($ns_args as $triple) {
545             list($ns_alias, $ns_key, $value) = $triple;
546             $ns_uri = $this->namespaces->getNamespaceURI($ns_alias);
547             if ($ns_uri === null) {
548                 $ns_uri = $this->_getDefaultNamespace($ns_alias);
549                 if ($ns_uri === null) {
550
551                     $ns_uri = Auth_OpenID_OPENID_NS;
552                     $ns_key = sprintf('%s.%s', $ns_alias, $ns_key);
553                 } else {
554                     $this->namespaces->addAlias($ns_uri, $ns_alias, true);
555                 }
556             }
557
558             $this->setArg($ns_uri, $ns_key, $value);
559         }
560
561         return true;
562     }
563
564     function _getDefaultNamespace($mystery_alias)
565     {
566         global $Auth_OpenID_registered_aliases;
567         if ($this->isOpenID1()) {
568             return @$Auth_OpenID_registered_aliases[$mystery_alias];
569         }
570         return null;
571     }
572
573     function setOpenIDNamespace($openid_ns_uri, $implicit)
574     {
575         if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) {
576             Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri);
577             return false;
578         }
579
580         $succeeded = $this->namespaces->addAlias($openid_ns_uri,
581                                                  Auth_OpenID_NULL_NAMESPACE,
582                                                  $implicit);
583         if ($succeeded === false) {
584             return false;
585         }
586
587         $this->_openid_ns_uri = $openid_ns_uri;
588
589         return true;
590     }
591
592     function getOpenIDNamespace()
593     {
594         return $this->_openid_ns_uri;
595     }
596
597     function fromKVForm($kvform_string)
598     {
599         // Create a Message from a KVForm string
600         return Auth_OpenID_Message::fromOpenIDArgs(
601                      Auth_OpenID_KVForm::toArray($kvform_string));
602     }
603
604     function copy()
605     {
606         return $this;
607     }
608
609     function toPostArgs()
610     {
611         // Return all arguments with openid. in front of namespaced
612         // arguments.
613
614         $args = array();
615
616         // Add namespace definitions to the output
617         foreach ($this->namespaces->iteritems() as $pair) {
618             list($ns_uri, $alias) = $pair;
619             if ($this->namespaces->isImplicit($ns_uri)) {
620                 continue;
621             }
622             if ($alias == Auth_OpenID_NULL_NAMESPACE) {
623                 $ns_key = 'openid.ns';
624             } else {
625                 $ns_key = 'openid.ns.' . $alias;
626             }
627             $args[$ns_key] = $ns_uri;
628         }
629
630         foreach ($this->args->items() as $pair) {
631             list($ns_parts, $value) = $pair;
632             list($ns_uri, $ns_key) = $ns_parts;
633             $key = $this->getKey($ns_uri, $ns_key);
634             $args[$key] = $value;
635         }
636
637         return $args;
638     }
639
640     function toArgs()
641     {
642         // Return all namespaced arguments, failing if any
643         // non-namespaced arguments exist.
644         $post_args = $this->toPostArgs();
645         $kvargs = array();
646         foreach ($post_args as $k => $v) {
647             if (strpos($k, 'openid.') !== 0) {
648                 // raise ValueError(
649                 //   'This message can only be encoded as a POST, because it '
650                 //   'contains arguments that are not prefixed with "openid."')
651                 return null;
652             } else {
653                 $kvargs[substr($k, 7)] = $v;
654             }
655         }
656
657         return $kvargs;
658     }
659
660     function toFormMarkup($action_url, $form_tag_attrs = null,
661                           $submit_text = "Continue")
662     {
663         $form = "<form accept-charset=\"UTF-8\" ".
664             "enctype=\"application/x-www-form-urlencoded\"";
665
666         if (!$form_tag_attrs) {
667             $form_tag_attrs = array();
668         }
669
670         $form_tag_attrs['action'] = $action_url;
671         $form_tag_attrs['method'] = 'post';
672
673         unset($form_tag_attrs['enctype']);
674         unset($form_tag_attrs['accept-charset']);
675
676         if ($form_tag_attrs) {
677             foreach ($form_tag_attrs as $name => $attr) {
678                 $form .= sprintf(" %s=\"%s\"", $name, $attr);
679             }
680         }
681
682         $form .= ">\n";
683
684         foreach ($this->toPostArgs() as $name => $value) {
685             $form .= sprintf(
686                         "<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
687                         $name, $value);
688         }
689
690         $form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
691                          $submit_text);
692
693         $form .= "</form>\n";
694
695         return $form;
696     }
697
698     function toURL($base_url)
699     {
700         // Generate a GET URL with the parameters in this message
701         // attached as query parameters.
702         return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
703     }
704
705     function toKVForm()
706     {
707         // Generate a KVForm string that contains the parameters in
708         // this message. This will fail if the message contains
709         // arguments outside of the 'openid.' prefix.
710         return Auth_OpenID_KVForm::fromArray($this->toArgs());
711     }
712
713     function toURLEncoded()
714     {
715         // Generate an x-www-urlencoded string
716         $args = array();
717
718         foreach ($this->toPostArgs() as $k => $v) {
719             $args[] = array($k, $v);
720         }
721
722         sort($args);
723         return Auth_OpenID::httpBuildQuery($args);
724     }
725
726     /**
727      * @access private
728      */
729     function _fixNS($namespace)
730     {
731         // Convert an input value into the internally used values of
732         // this object
733
734         if ($namespace == Auth_OpenID_OPENID_NS) {
735             if ($this->_openid_ns_uri === null) {
736                 return new Auth_OpenID_FailureResponse(null,
737                     'OpenID namespace not set');
738             } else {
739                 $namespace = $this->_openid_ns_uri;
740             }
741         }
742
743         if (($namespace != Auth_OpenID_BARE_NS) &&
744               (!is_string($namespace))) {
745             //TypeError
746             $err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ".
747                               "Auth_OpenID_OPENID_NS or a string. got %s",
748                               print_r($namespace, true));
749             return new Auth_OpenID_FailureResponse(null, $err_msg);
750         }
751
752         if (($namespace != Auth_OpenID_BARE_NS) &&
753             (strpos($namespace, ':') === false)) {
754             // fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r'
755             // warnings.warn(fmt % (namespace,), DeprecationWarning)
756
757             if ($namespace == 'sreg') {
758                 // fmt = 'Using %r instead of "sreg" as namespace'
759                 // warnings.warn(fmt % (SREG_URI,), DeprecationWarning,)
760                 return Auth_OpenID_SREG_URI;
761             }
762         }
763
764         return $namespace;
765     }
766
767     function hasKey($namespace, $ns_key)
768     {
769         $namespace = $this->_fixNS($namespace);
770         if (Auth_OpenID::isFailure($namespace)) {
771             // XXX log me
772             return false;
773         } else {
774             return $this->args->contains(array($namespace, $ns_key));
775         }
776     }
777
778     function getKey($namespace, $ns_key)
779     {
780         // Get the key for a particular namespaced argument
781         $namespace = $this->_fixNS($namespace);
782         if (Auth_OpenID::isFailure($namespace)) {
783             return $namespace;
784         }
785         if ($namespace == Auth_OpenID_BARE_NS) {
786             return $ns_key;
787         }
788
789         $ns_alias = $this->namespaces->getAlias($namespace);
790
791         // No alias is defined, so no key can exist
792         if ($ns_alias === null) {
793             return null;
794         }
795
796         if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
797             $tail = $ns_key;
798         } else {
799             $tail = sprintf('%s.%s', $ns_alias, $ns_key);
800         }
801
802         return 'openid.' . $tail;
803     }
804
805     function getArg($namespace, $key, $default = null)
806     {
807         // Get a value for a namespaced key.
808         $namespace = $this->_fixNS($namespace);
809
810         if (Auth_OpenID::isFailure($namespace)) {
811             return $namespace;
812         } else {
813             if ((!$this->args->contains(array($namespace, $key))) &&
814               ($default == Auth_OpenID_NO_DEFAULT)) {
815                 $err_msg = sprintf("Namespace %s missing required field %s",
816                                    $namespace, $key);
817                 return new Auth_OpenID_FailureResponse(null, $err_msg);
818             } else {
819                 return $this->args->get(array($namespace, $key), $default);
820             }
821         }
822     }
823
824     function getArgs($namespace)
825     {
826         // Get the arguments that are defined for this namespace URI
827
828         $namespace = $this->_fixNS($namespace);
829         if (Auth_OpenID::isFailure($namespace)) {
830             return $namespace;
831         } else {
832             $stuff = array();
833             foreach ($this->args->items() as $pair) {
834                 list($key, $value) = $pair;
835                 list($pair_ns, $ns_key) = $key;
836                 if ($pair_ns == $namespace) {
837                     $stuff[$ns_key] = $value;
838                 }
839             }
840
841             return $stuff;
842         }
843     }
844
845     function updateArgs($namespace, $updates)
846     {
847         // Set multiple key/value pairs in one call
848
849         $namespace = $this->_fixNS($namespace);
850
851         if (Auth_OpenID::isFailure($namespace)) {
852             return $namespace;
853         } else {
854             foreach ($updates as $k => $v) {
855                 $this->setArg($namespace, $k, $v);
856             }
857             return true;
858         }
859     }
860
861     function setArg($namespace, $key, $value)
862     {
863         // Set a single argument in this namespace
864         $namespace = $this->_fixNS($namespace);
865
866         if (Auth_OpenID::isFailure($namespace)) {
867             return $namespace;
868         } else {
869             $this->args->set(array($namespace, $key), $value);
870             if ($namespace !== Auth_OpenID_BARE_NS) {
871                 $this->namespaces->add($namespace);
872             }
873             return true;
874         }
875     }
876
877     function delArg($namespace, $key)
878     {
879         $namespace = $this->_fixNS($namespace);
880
881         if (Auth_OpenID::isFailure($namespace)) {
882             return $namespace;
883         } else {
884             return $this->args->del(array($namespace, $key));
885         }
886     }
887
888     function getAliasedArg($aliased_key, $default = null)
889     {
890         if ($aliased_key == 'ns') {
891             // Return the namespace URI for the OpenID namespace
892             return $this->getOpenIDNamespace();
893         }
894
895         $parts = explode('.', $aliased_key, 2);
896
897         if (count($parts) != 2) {
898             $ns = null;
899         } else {
900             list($alias, $key) = $parts;
901
902             if ($alias == 'ns') {
903               // Return the namespace URI for a namespace alias
904               // parameter.
905               return $this->namespaces->getNamespaceURI($key);
906             } else {
907               $ns = $this->namespaces->getNamespaceURI($alias);
908             }
909         }
910
911         if ($ns === null) {
912             $key = $aliased_key;
913             $ns = $this->getOpenIDNamespace();
914         }
915
916         return $this->getArg($ns, $key, $default);
917     }
918 }
919
920 ?>