using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using DotNetOpenId.Provider;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Diagnostics;
namespace DotNetOpenId {
///
/// A message did not conform to the OpenID protocol, or
/// some other processing error occurred.
///
[Serializable]
public class OpenIdException : Exception, IEncodable {
internal IDictionary Query;
///
/// An Identifier (claimed or local provider) that was being processed when
/// the exception was thrown.
///
public Identifier Identifier { get; private set; }
internal Protocol Protocol = Protocol.Default;
Protocol IEncodable.Protocol { get { return this.Protocol; } }
internal IDictionary ExtraArgsToReturn;
internal OpenIdException(string message, Identifier identifier, IDictionary query, Exception innerException)
: base(message, innerException) {
this.Query = query;
Identifier = identifier;
if (query != null) Protocol = Protocol.Detect(query);
if (query != null) {
Logger.ErrorFormat("OpenIdException: {0}{1}{2}", message, Environment.NewLine, Util.ToString(query));
} else {
Logger.ErrorFormat("OpenIdException: {0}", message);
}
}
internal OpenIdException(string message, Identifier identifier, IDictionary query)
: this(message, identifier, query, null) {
}
internal OpenIdException(string message, Identifier identifier, Exception innerException)
: this(message, identifier, null, innerException) {
}
internal OpenIdException(string message, Identifier identifier)
: this(message, identifier, null, null) {
}
internal OpenIdException(string message, IDictionary query)
: this(message, null, query, null) {
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")]
internal OpenIdException(string message, Exception innerException)
: this(message, null, null, innerException) {
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")]
internal OpenIdException(string message)
: this(message, null, null, null) {
}
///
/// Instantiates an based on deserialized data.
///
///
///
protected OpenIdException(SerializationInfo info, StreamingContext context)
: base(info, context) {
Query = (IDictionary)info.GetValue("query", typeof(IDictionary));
Identifier = (Identifier)info.GetValue("Identifier", typeof(Identifier));
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")]
internal OpenIdException() { }
///
/// Serializes the exception details for binary transmission.
///
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public override void GetObjectData(SerializationInfo info, StreamingContext context) {
base.GetObjectData(info, context);
info.AddValue("query", Query, typeof(IDictionary));
info.AddValue("Identifier", Identifier, typeof(Identifier));
}
internal bool HasReturnTo {
get {
return Query == null ? false : Query.ContainsKey(Protocol.openid.return_to);
}
}
#region IEncodable Members
///
/// Gets whether this exception was generated on an OP as the result of processing a message
/// that came directly from the RP.
///
///
/// This is useful because it allows us to determine what kind of error reporting we'll send
/// in the HTTP response.
///
private bool IsDirectMessage {
get {
Debug.Assert(Query != null, "An OpenId exception should always be provided with the query if it is to be encoded for transmittal to the RP.");
if (Query != null) {
string mode = Util.GetOptionalArg(Query, Protocol.openid.mode);
if (mode != null) {
return mode == Protocol.Args.Mode.associate ||
mode == Protocol.Args.Mode.check_authentication;
}
}
// Unable to figure it out, so we'll default to indirect message.
return false;
}
}
EncodingType IEncodable.EncodingType {
get {
if (IsDirectMessage)
return EncodingType.DirectResponse;
if (HasReturnTo)
return EncodingType.IndirectMessage;
Debug.Fail("Somehow we cannot tell whether this is a direct message or indirect message. Did we construct an exception without a Query parameter?");
return EncodingType.None;
}
}
///
/// Fields that should be encoded for processing when this exception
/// is thrown by a Provider and the details should be passed to the
/// relying party.
///
public IDictionary EncodedFields {
get {
var q = new Dictionary();
if (IsDirectMessage) {
q.Add(Protocol.openidnp.mode, Protocol.Args.Mode.error);
q.Add(Protocol.openidnp.error, Message);
} else {
q.Add(Protocol.openid.mode, Protocol.Args.Mode.error);
q.Add(Protocol.openid.error, Message);
}
if (ExtraArgsToReturn != null) {
foreach (var pair in ExtraArgsToReturn) {
q.Add(pair.Key, pair.Value);
}
}
return q;
}
}
///
/// The URL that the exception details should be forwarded to.
/// This is used when a Provider throws an exception that a relying
/// party may find helpful in diagnosing the failure.
///
public Uri RedirectUrl {
get {
if (Query == null)
return null;
return new Uri(Util.GetRequiredArg(Query, Protocol.openid.return_to));
}
}
#endregion
}
}