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 */
12require_once 'Auth/OpenID.php';
13require_once 'Auth/OpenID/KVForm.php';
14require_once 'Auth/Yadis/XML.php';
15require_once 'Auth/OpenID/Consumer.php'; // For Auth_OpenID_FailureResponse
16
17// This doesn't REALLY belong here, but where is better?
18define('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
23define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
24
25// The OpenID 1.X namespace URI
26define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
27define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
28
29function 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
36define('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.
40define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
41
42// The null namespace, when it is an allowed OpenID namespace
43define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
44
45// The top-level namespace, excluding all pairs with keys that start
46// with "openid."
47define('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.
51define('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.
55define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
56
57// All OpenID protocol fields.  Used to check namespace aliases.
58global $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.
68global $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 */
78function 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 */
105function 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 */
125class 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    static 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 */
276class 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 */
415class 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    static 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    static 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    static 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, urldecode($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
921