summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OpenId/Xrds/XrdElement.cs
blob: 4645ad1b26eae223b31a0f8a4ce804a46dec5dff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//-----------------------------------------------------------------------
// <copyright file="XrdElement.cs" company="Outercurve Foundation, Scott Hanselman">
//     Copyright (c) Outercurve Foundation, Scott Hanselman. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace DotNetOpenAuth.Xrds {
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Xml.XPath;
	using DotNetOpenAuth.Messaging;
	using DotNetOpenAuth.OpenId;

	/// <summary>
	/// The Xrd element in an XRDS document.
	/// </summary>
	internal class XrdElement : XrdsNode {
		/// <summary>
		/// Initializes a new instance of the <see cref="XrdElement"/> class.
		/// </summary>
		/// <param name="xrdElement">The XRD element.</param>
		/// <param name="parent">The parent.</param>
		public XrdElement(XPathNavigator xrdElement, XrdsDocument parent) :
			base(xrdElement, parent) {
		}

		/// <summary>
		/// Gets the child service elements.
		/// </summary>
		/// <value>The services.</value>
		public IEnumerable<ServiceElement> Services {
			get {
				// We should enumerate them in priority order
				List<ServiceElement> services = new List<ServiceElement>();
				foreach (XPathNavigator node in Node.Select("xrd:Service", XmlNamespaceResolver)) {
					services.Add(new ServiceElement(node, this));
				}
				services.Sort();
				return services;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this XRD element's resolution at the XRI resolver was successful.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this XRD's resolution was successful; otherwise, <c>false</c>.
		/// </value>
		public bool IsXriResolutionSuccessful {
			get {
				return this.XriResolutionStatusCode == 100;
			}
		}

		/// <summary>
		/// Gets the canonical ID (i-number) for this element.
		/// </summary>
		public string CanonicalID {
			get {
				var n = Node.SelectSingleNode("xrd:CanonicalID", XmlNamespaceResolver);
				return n != null ? n.Value : null;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="CanonicalID"/> was verified.
		/// </summary>
		public bool IsCanonicalIdVerified {
			get {
				var n = Node.SelectSingleNode("xrd:Status", XmlNamespaceResolver);
				return n != null && string.Equals(n.GetAttribute("cid", string.Empty), "verified", StringComparison.Ordinal);
			}
		}

		/// <summary>
		/// Gets the services for OP Identifiers.
		/// </summary>
		public IEnumerable<ServiceElement> OpenIdProviderIdentifierServices {
			get { return this.SearchForServiceTypeUris(p => p.OPIdentifierServiceTypeURI); }
		}

		/// <summary>
		/// Gets the services for Claimed Identifiers.
		/// </summary>
		public IEnumerable<ServiceElement> OpenIdClaimedIdentifierServices {
			get { return this.SearchForServiceTypeUris(p => p.ClaimedIdentifierServiceTypeURI); }
		}

		/// <summary>
		/// Gets the services that would be discoverable at an RP for return_to verification.
		/// </summary>
		public IEnumerable<ServiceElement> OpenIdRelyingPartyReturnToServices {
			get { return this.SearchForServiceTypeUris(p => p.RPReturnToTypeURI); }
		}

		/// <summary>
		/// Gets the services that would be discoverable at an RP for the UI extension icon.
		/// </summary>
		public IEnumerable<ServiceElement> OpenIdRelyingPartyIcons {
			get { return this.SearchForServiceTypeUris(p => "http://specs.openid.net/extensions/ui/icon"); }
		}

		/// <summary>
		/// Gets an enumeration of all Service/URI elements, sorted in priority order.
		/// </summary>
		public IEnumerable<UriElement> ServiceUris {
			get {
				return from service in this.Services
					   from uri in service.UriElements
					   select uri;
			}
		}

		/// <summary>
		/// Gets the XRI resolution status code.
		/// </summary>
		private int XriResolutionStatusCode {
			get {
				var n = Node.SelectSingleNode("xrd:Status", XmlNamespaceResolver);
				string codeString = null;
				ErrorUtilities.VerifyProtocol(n != null && !string.IsNullOrEmpty(codeString = n.GetAttribute("code", string.Empty)), XrdsStrings.XriResolutionStatusMissing);
				int code;
				ErrorUtilities.VerifyProtocol(int.TryParse(codeString, out code) && code >= 100 && code < 400, XrdsStrings.XriResolutionStatusMissing);
				return code;
			}
		}

		/// <summary>
		/// Searches for service sub-elements that have Type URI sub-elements that match
		/// one that we have for a known OpenID protocol version.
		/// </summary>
		/// <param name="p">A function that selects what element of the OpenID Protocol we're interested in finding.</param>
		/// <returns>A sequence of service elements that match the search criteria, sorted in XRDS @priority attribute order.</returns>
		internal IEnumerable<ServiceElement> SearchForServiceTypeUris(Func<Protocol, string> p) {
			var xpath = new StringBuilder();
			xpath.Append("xrd:Service[");
			foreach (var protocol in Protocol.AllVersions) {
				string typeUri = p(protocol);
				if (typeUri == null) {
					continue;
				}
				xpath.Append("xrd:Type/text()='");
				xpath.Append(typeUri);
				xpath.Append("' or ");
			}
			xpath.Length -= 4;
			xpath.Append("]");
			var services = new List<ServiceElement>();
			foreach (XPathNavigator service in Node.Select(xpath.ToString(), XmlNamespaceResolver)) {
				services.Add(new ServiceElement(service, this));
			}

			// Put the services in their own defined priority order
			services.Sort();
			return services;
		}
	}
}