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