using System;
using System.Collections.Generic;
using System.Text;
using DotNetOpenId.RelyingParty;
using System.Globalization;
using System.Diagnostics;
namespace DotNetOpenId.Extensions.AttributeExchange {
///
/// The Attribute Exchange Fetch message, response leg.
///
public sealed class FetchResponse : IExtensionResponse {
readonly string Mode = "fetch_response";
List attributesProvided = new List();
///
/// Enumerates over all the attributes included by the Provider.
///
public IEnumerable Attributes {
get { return attributesProvided; }
}
///
/// Used by the Provider to add attributes to the response for the relying party.
///
public void AddAttribute(AttributeValues attribute) {
if (attribute == null) throw new ArgumentNullException("attribute");
if (containsAttribute(attribute.TypeUri)) throw new ArgumentException(
string.Format(CultureInfo.CurrentCulture, Strings.AttributeAlreadyAdded, attribute.TypeUri));
attributesProvided.Add(attribute);
}
///
/// Used by the Relying Party to get the value(s) returned by the OpenID Provider
/// for a given attribute, or null if that attribute was not provided.
///
public AttributeValues GetAttribute(string attributeTypeUri) {
foreach (var att in attributesProvided) {
if (att.TypeUri == attributeTypeUri)
return att;
}
return null;
}
bool containsAttribute(string typeUri) {
return GetAttribute(typeUri) != null;
}
///
/// Whether the OpenID Provider intends to honor the request for updates.
///
public bool UpdateUrlSupported { get { return UpdateUrl != null; } }
///
/// The URL the OpenID Provider will post updates to. Must be set if the Provider
/// supports and will use this feature.
///
public Uri UpdateUrl { get; set; }
#region IExtensionResponse Members
string IExtension.TypeUri { get { return Constants.TypeUri; } }
IEnumerable IExtension.AdditionalSupportedTypeUris {
get { return new string[0]; }
}
IDictionary IExtensionResponse.Serialize(Provider.IRequest authenticationRequest) {
var fields = new Dictionary {
{ "mode", Mode },
};
if (UpdateUrlSupported)
fields.Add("update_url", UpdateUrl.AbsoluteUri);
SerializeAttributes(fields, attributesProvided);
return fields;
}
internal static void SerializeAttributes(Dictionary fields, IEnumerable attributes) {
Debug.Assert(fields != null && attributes != null);
AliasManager aliasManager = new AliasManager();
foreach (var att in attributes) {
string alias = aliasManager.GetAlias(att.TypeUri);
fields.Add("type." + alias, att.TypeUri);
if (att.Values == null) continue;
if (att.Values.Count != 1) {
fields.Add("count." + alias, att.Values.Count.ToString(CultureInfo.InvariantCulture));
for (int i = 0; i < att.Values.Count; i++) {
fields.Add(string.Format(CultureInfo.InvariantCulture, "value.{0}.{1}", alias, i + 1), att.Values[i]);
}
} else {
fields.Add("value." + alias, att.Values[0]);
}
}
}
bool IExtensionResponse.Deserialize(IDictionary fields, IAuthenticationResponse response, string typeUri) {
if (fields == null) return false;
string mode;
fields.TryGetValue("mode", out mode);
if (mode != Mode) return false;
string updateUrl;
fields.TryGetValue("update_url", out updateUrl);
Uri updateUri;
if (Uri.TryCreate(updateUrl, UriKind.Absolute, out updateUri))
UpdateUrl = updateUri;
foreach (var att in DeserializeAttributes(fields))
AddAttribute(att);
return true;
}
internal static IEnumerable DeserializeAttributes(IDictionary fields) {
AliasManager aliasManager = parseAliases(fields);
foreach (string alias in aliasManager.Aliases) {
AttributeValues att = new AttributeValues(aliasManager.ResolveAlias(alias));
int count = 1;
bool countSent = false;
string countString;
if (fields.TryGetValue("count." + alias, out countString)) {
if (!int.TryParse(countString, out count) || count < 0) {
Logger.ErrorFormat("Failed to parse count.{0} value to a non-negative integer.", alias);
continue;
}
countSent = true;
}
if (countSent) {
for (int i = 1; i <= count; i++) {
string value;
if (fields.TryGetValue(string.Format(CultureInfo.InvariantCulture, "value.{0}.{1}", alias, i), out value)) {
att.Values.Add(value);
} else {
Logger.ErrorFormat("Missing value for attribute '{0}'.", att.TypeUri);
continue;
}
}
} else {
string value;
if (fields.TryGetValue("value." + alias, out value))
att.Values.Add(value);
else {
Logger.ErrorFormat("Missing value for attribute '{0}'.", att.TypeUri);
continue;
}
}
yield return att;
}
}
static AliasManager parseAliases(IDictionary fields) {
Debug.Assert(fields != null);
AliasManager aliasManager = new AliasManager();
foreach (var pair in fields) {
if (!pair.Key.StartsWith("type.", StringComparison.Ordinal)) continue;
string alias = pair.Key.Substring(5);
if (alias.IndexOfAny(new[] { '.', ',', ':' }) >= 0) {
Logger.ErrorFormat("Illegal characters in alias name '{0}'.", alias);
continue;
}
aliasManager.SetAlias(alias, pair.Value);
}
return aliasManager;
}
#endregion
}
}