static StateContainer SetupContainer()
        {
            StateContainer props = new StateContainer();

            new Xrds(props);
            new Yadis(props);
            new Html(props);

            new IdentityAuthentication(props);

            return props;
        }
        /// <summary>
        /// Returns the URL to which the User Agent should be redirected for the initial authentication request.
        /// </summary>
        /// <param name="props">RequestProperties object holding the current state.</param>
        /// <param name="discResult">The DiscoveryResult object created from the previous discovery process.</param>
        /// <param name="immediate">Whether or not an Immediate Mode request is being generated.</param>
        /// <returns>The complete URL to which the User Agent should be redirected.</returns>
        internal static Uri GetRedirectURL(StateContainer props, DiscoveryResult discResult, bool immediate)
        {
            if (props == null) { throw new ArgumentNullException("props"); }
            if (discResult == null) { throw new ArgumentNullException("discResult"); }

            NameValueCollection pms = new NameValueCollection();

            if (immediate)
            {
                pms["openid.mode"] = "checkid_immediate";
            }
            else
            {
                pms["openid.mode"] = "checkid_setup";
            }

            string returl = props.ReturnToUrl.AbsoluteUri;

            switch (props.AuthMode)
            {
                case AuthenticationMode.Stateful:
                    Association assoc = props.AssociationManager.FindByServer(discResult.ServerUrl.AbsoluteUri);

                    Random r = new Random();
                    props.Nonce = r.Next();

                    switch (discResult.AuthVersion)
                    {
                        case ProtocolVersion.V1Dot1:
                            pms["openid.assoc_handle"] = assoc.Handle;
                            pms["openid.trust_root"] = props.TrustRoot;

                            break;
                        case ProtocolVersion.V2Dot0:
                            pms["openid.ns"] = ProtocolUri.OpenId2Dot0.AbsoluteUri;
                            pms["openid.assoc_handle"] = assoc.Handle;
                            pms["openid.realm"] = props.TrustRoot;
                            break;
                    }
                    break;
                case AuthenticationMode.Stateless:
                    switch (discResult.AuthVersion)
                    {
                        case ProtocolVersion.V1Dot1:
                            pms["openid.trust_root"] = props.TrustRoot;
                            break;
                        case ProtocolVersion.V2Dot0:
                            pms["openid.ns"] = ProtocolUri.OpenId2Dot0.AbsoluteUri;
                            pms["openid.realm"] = props.TrustRoot;
                            break;
                    }
                    break;
            }

            foreach (IExtension e in props.ExtensionPlugIns)
            {
                NameValueCollection authdata = e.BuildAuthorizationData(discResult);
                foreach (string key in authdata.Keys)
                {
                    if (key == "esoid.ReturnUrl")
                    {
                        if (returl.Contains("?"))
                        {
                            returl += "&" + authdata[key];
                        }
                        else
                        {
                            returl += "?" + authdata[key];
                        }
                    }
                    else
                    {
                        pms[key] = authdata[key];
                    }
                }
            }

            pms["openid.return_to"] = Utility.AddExtraVariables(returl, props);
            return Utility.MakeGetURL(discResult.ServerUrl, pms);
        }
        /// <summary>
        /// Validate a stateless mode response from an OpenID Provider.
        /// </summary>
        /// <param name="server">The OpenID Provider</param>
        /// <param name="fallback">Whether or not this is a fallback check due to a failed stateful validation attempt.</param>
        /// <param name="rp">The StateContainer object containing the arguments to process.</param>
        /// <returns>True if the validation is successful, false if not.</returns>
        internal static bool ValidateStatelessResponse(Uri server, bool fallback, StateContainer rp)
        {
            if (server == null) { throw new ArgumentNullException("server"); }
            if (rp == null) { throw new ArgumentNullException("rp"); }

            NameValueCollection pms = new NameValueCollection();
            if (fallback)
            {
                if (!String.IsNullOrEmpty(rp.RequestArguments["openid.invalidate_handle"]))
                {
                    pms["openid.invalidate_handle"] = rp.RequestArguments["openid.invalidate_handle"];
                }
            }

            // Run validation checks in each plug-in
            foreach (IExtension ext in rp.ExtensionPlugIns)
            {
                if (!ext.Validation()) { return false; }
            }

            // Send only required parameters to confirm validity
            string[] arr_signed = rp.RequestArguments["openid.signed"].Split(',');
            for (int i = 0; i < arr_signed.Length; i++)
            {
                string s = arr_signed[i];
                string c = rp.RequestArguments["openid." + arr_signed[i]];
                pms["openid." + s] = c;
            }
            if (server == null)
            {
                Tracer.Write("No OpenID servers found");
                rp.ErrorState = ErrorCondition.NoServersFound;
                return false;
            }

            pms["openid.mode"] = "check_authentication";
            pms["openid.assoc_handle"] = rp.RequestArguments["openid.assoc_handle"];
            pms["openid.signed"] = rp.RequestArguments["openid.signed"];
            pms["openid.sig"] = rp.RequestArguments["openid.sig"];

            string actualLocation = null;
            // Connect to IdP
            string response = Utility.MakeRequest(server, "POST", pms, out actualLocation);
    
            if (String.IsNullOrEmpty(response))
            {
                Tracer.Write("No response from Identity Provider using POST, trying GET.");
                response = Utility.MakeRequest(server, "GET", pms, out actualLocation);
                if (String.IsNullOrEmpty(response))
                {
                    return false;
                }
            }

            // Parse reponse
            NameValueCollection data = Utility.SplitResponse(response);

            // Check for validity of authentication request
            if (data["is_valid"] == "true")
            {
                Tracer.Write("Server has validated authentication response.");
                return true;
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                foreach (string key in data.Keys)
                {
                    sb.AppendLine(key + ":" + data[key]);
                }
                Tracer.Write("Server has not validated authentication response.  Response received: " + sb.ToString());
                rp.ErrorState = ErrorCondition.RequestRefused;
                return false;
            }
        }
        /// <summary>
        /// Validate a stateful mode response from an OpenID Provider.
        /// </summary>
        /// <param name="server">The OpenID Provider URL.</param>
        /// <param name="rp">The StateContainer object needed to process the validation.</param>
        /// <returns>Whether or not validation was successful.</returns>
        internal static bool ValidateStatefulResponse(Uri server, StateContainer rp) 
        {
            if (server == null) { throw new ArgumentNullException("server"); }
            if (rp == null) { throw new ArgumentNullException("rp"); }

            if (rp.Nonce == -1)
            {
                Tracer.Write("Error: The cnonce is not valid.");
                rp.ErrorState = ErrorCondition.SessionTimeout;
                return false;
            }
            else
            {
                if (!String.IsNullOrEmpty(rp.RequestArguments["cnonce"]) && rp.Nonce != Convert.ToInt32(rp.RequestArguments["cnonce"], CultureInfo.InvariantCulture))
                {
                    Tracer.Write("Error: The cnonce has expired.");
                    rp.ErrorState = ErrorCondition.SessionTimeout;
                    return false;
                }
            }

            // Run validation checks in each plug-in
            foreach (IExtension ext in rp.ExtensionPlugIns)
            {
                if (!ext.Validation()) { return false; }
            }

            Tracer.Write("Looking up association in association table.");
            Association assoc = rp.AssociationManager.FindByHandle(rp.RequestArguments["openid.assoc_handle"]);

            if (assoc == null)
            {
                // Check to see if the handle has been invalidated
                if (rp.RequestArguments["openid.invalidate_handle"] != null)
                {
                    Tracer.Write("Association handle has been invalidated.");
                    return false;
                }
                else
                {
                    Tracer.Write("Association handle was not found in the table.");
                    return false;
                }
            }

            // Ensure association key has not expired
            if (assoc.Expiration < DateTime.UtcNow)
            {
                Tracer.Write("Association has expired, removing from table.");
                rp.AssociationManager.Remove(assoc);
                return false;
            }

            // Check for someone trying to forge a response from another
            // OpenID Provider
            if (server.AbsoluteUri != assoc.Server)
            {
                Tracer.Write("Received response handle is not valid for the specified OpenID Provider.");
                return false;
            }

            // Compare data from browser to association handle from server
            if (assoc.Handle == rp.RequestArguments["openid.assoc_handle"])
            {
                string[] tokens = rp.RequestArguments["openid.signed"].ToString().Split(',');
                string token_contents = "";
                foreach (string token in tokens)
                {
                    token_contents += token + ":" + rp.RequestArguments["openid." + token] + "\n";
                }
                Tracer.Write("Generating signature for tokens: " + token_contents);
                string sig = rp.RequestArguments["openid.sig"].ToString();

                byte[] secretkey = assoc.Secret;
                byte[] tokenbyte = ASCIIEncoding.ASCII.GetBytes(token_contents);
                HashAlgorithm hmac = null;

                if (assoc.AssociationType == "HMAC-SHA1")
                {
                    hmac = new HMACSHA1(secretkey);
                }
                else if (assoc.AssociationType == "HMAC-SHA256")
                {
                    hmac = new HMACSHA256(secretkey);
                }
                byte[] realHash = hmac.ComputeHash(tokenbyte);
                string strrealHash = Convert.ToBase64String(realHash);

                Tracer.Write("Expected signature: " + sig);
                Tracer.Write("Generated signature: " + strrealHash);

                if (sig != strrealHash) 
                { 
                    Tracer.Write("Received signature does not match generated signature");
                    return false;
                }
                return true;
            }
            Tracer.Write("Received association handle does not match cached handle.");
            return false;
        }
        /// <summary>
        /// Processes errors received during HTTP requests.
        /// </summary>
        /// <param name="response">WebResponse object to handle.</param>
        /// <param name="rp">StateContainer object where error state will be recorded.</param>
        internal static void HandleHttpError(WebResponse response, StateContainer rp)
        {
            Stream s;
            StreamReader sr;
            string body = "";
            if (response == null)
            {
                Tracer.Write("Error: Received null response to HTTP request.");
                if (rp != null) rp.ErrorState = ErrorCondition.HttpError;
                return;
            }

            HttpWebResponse hwr = (HttpWebResponse)response;
            switch (hwr.StatusCode)
            {
                case HttpStatusCode.BadRequest:
                    s = response.GetResponseStream();
                    sr = new StreamReader(s);
                    body = sr.ReadToEnd();
                    if (body != null)
                    {
                        NameValueCollection data = SplitResponse(body);
                        if (data["error"] != null)
                        {
                            Tracer.Write("Received error: " + data["error"]);
                            if (rp != null) rp.ErrorState = ErrorCondition.RequestRefused;
                            return;
                        }
                    }
                    if (rp != null) rp.ErrorState = ErrorCondition.HttpError;
                    break;
                default:
                    Tracer.Write("HTTP request failed, response code: " + hwr.StatusCode.ToString());
                    break;
            }
        }
        /// <summary>
        /// Appends the cnonce variable to the end of a URL.
        /// </summary>
        /// <param name="url">URL to process.</param>
        /// <param name="rp">StateContainer to use.</param>
        /// <returns>A combined URL.</returns>
        internal static string AddExtraVariables(string url, StateContainer rp)
        {
            if (String.IsNullOrEmpty(url)) { return url; }
            if (rp == null) { throw new ArgumentNullException("rp"); }

            string retval = url;
            if (rp.AuthMode == AuthenticationMode.Stateful)
            {
                if (retval.Contains("?"))
                {
                    retval += "&cnonce=" + rp.Nonce;
                }
                else
                {
                    retval += "?cnonce=" + rp.Nonce;
                }
            }

            return retval;
        }
 /// <summary>
 /// Look at the current arguments and load the extension plugins needed to service
 /// the request.
 /// </summary>
 /// <param name="rp">StateContainer object to store loaded plugins.</param>
 internal static void AutoLoadExtensionPlugins(StateContainer rp)
 {
     if (rp == null) { throw new ArgumentNullException("rp"); }
     Tracer.Write("Loading extension plugins");
     Type[] types = GetRequiredExtensionPlugins(rp.RequestArguments);
     foreach (Type t in types)
     {
         Tracer.Write("Loading plugin '" + t.ToString() + "'");
         Activator.CreateInstance(t, rp);
     }
 }