aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/UserServer.Modules/OpenIdService.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Grid/UserServer.Modules/OpenIdService.cs (renamed from OpenSim/Grid/UserServer/OpenIdService.cs)674
1 files changed, 337 insertions, 337 deletions
diff --git a/OpenSim/Grid/UserServer/OpenIdService.cs b/OpenSim/Grid/UserServer.Modules/OpenIdService.cs
index 187758f..5c8501f 100644
--- a/OpenSim/Grid/UserServer/OpenIdService.cs
+++ b/OpenSim/Grid/UserServer.Modules/OpenIdService.cs
@@ -1,337 +1,337 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met: 6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright 7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright 9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the 12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products 13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission. 14 * derived from this software without specific prior written permission.
15 * 15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Collections.Specialized; 30using System.Collections.Specialized;
31using System.IO; 31using System.IO;
32using System.Net; 32using System.Net;
33using System.Web; 33using System.Web;
34using DotNetOpenId; 34using DotNetOpenId;
35using DotNetOpenId.Provider; 35using DotNetOpenId.Provider;
36using OpenSim.Framework; 36using OpenSim.Framework;
37using OpenSim.Framework.Servers; 37using OpenSim.Framework.Servers;
38 38
39namespace OpenSim.Grid.UserServer 39namespace OpenSim.Grid.UserServer.Modules
40{ 40{
41 /// <summary> 41 /// <summary>
42 /// Temporary, in-memory store for OpenID associations 42 /// Temporary, in-memory store for OpenID associations
43 /// </summary> 43 /// </summary>
44 public class ProviderMemoryStore : IAssociationStore<AssociationRelyingPartyType> 44 public class ProviderMemoryStore : IAssociationStore<AssociationRelyingPartyType>
45 { 45 {
46 private class AssociationItem 46 private class AssociationItem
47 { 47 {
48 public AssociationRelyingPartyType DistinguishingFactor; 48 public AssociationRelyingPartyType DistinguishingFactor;
49 public string Handle; 49 public string Handle;
50 public DateTime Expires; 50 public DateTime Expires;
51 public byte[] PrivateData; 51 public byte[] PrivateData;
52 } 52 }
53 53
54 Dictionary<string, AssociationItem> m_store = new Dictionary<string, AssociationItem>(); 54 Dictionary<string, AssociationItem> m_store = new Dictionary<string, AssociationItem>();
55 SortedList<DateTime, AssociationItem> m_sortedStore = new SortedList<DateTime, AssociationItem>(); 55 SortedList<DateTime, AssociationItem> m_sortedStore = new SortedList<DateTime, AssociationItem>();
56 object m_syncRoot = new object(); 56 object m_syncRoot = new object();
57 57
58 #region IAssociationStore<AssociationRelyingPartyType> Members 58 #region IAssociationStore<AssociationRelyingPartyType> Members
59 59
60 public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc) 60 public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc)
61 { 61 {
62 AssociationItem item = new AssociationItem(); 62 AssociationItem item = new AssociationItem();
63 item.DistinguishingFactor = distinguishingFactor; 63 item.DistinguishingFactor = distinguishingFactor;
64 item.Handle = assoc.Handle; 64 item.Handle = assoc.Handle;
65 item.Expires = assoc.Expires.ToLocalTime(); 65 item.Expires = assoc.Expires.ToLocalTime();
66 item.PrivateData = assoc.SerializePrivateData(); 66 item.PrivateData = assoc.SerializePrivateData();
67 67
68 lock (m_syncRoot) 68 lock (m_syncRoot)
69 { 69 {
70 m_store[item.Handle] = item; 70 m_store[item.Handle] = item;
71 m_sortedStore[item.Expires] = item; 71 m_sortedStore[item.Expires] = item;
72 } 72 }
73 } 73 }
74 74
75 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor) 75 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor)
76 { 76 {
77 lock (m_syncRoot) 77 lock (m_syncRoot)
78 { 78 {
79 if (m_sortedStore.Count > 0) 79 if (m_sortedStore.Count > 0)
80 { 80 {
81 AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1]; 81 AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1];
82 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData); 82 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
83 } 83 }
84 else 84 else
85 { 85 {
86 return null; 86 return null;
87 } 87 }
88 } 88 }
89 } 89 }
90 90
91 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle) 91 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
92 { 92 {
93 AssociationItem item; 93 AssociationItem item;
94 bool success = false; 94 bool success = false;
95 lock (m_syncRoot) 95 lock (m_syncRoot)
96 success = m_store.TryGetValue(handle, out item); 96 success = m_store.TryGetValue(handle, out item);
97 97
98 if (success) 98 if (success)
99 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData); 99 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
100 else 100 else
101 return null; 101 return null;
102 } 102 }
103 103
104 public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle) 104 public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
105 { 105 {
106 lock (m_syncRoot) 106 lock (m_syncRoot)
107 { 107 {
108 for (int i = 0; i < m_sortedStore.Values.Count; i++) 108 for (int i = 0; i < m_sortedStore.Values.Count; i++)
109 { 109 {
110 AssociationItem item = m_sortedStore.Values[i]; 110 AssociationItem item = m_sortedStore.Values[i];
111 if (item.Handle == handle) 111 if (item.Handle == handle)
112 { 112 {
113 m_sortedStore.RemoveAt(i); 113 m_sortedStore.RemoveAt(i);
114 break; 114 break;
115 } 115 }
116 } 116 }
117 117
118 return m_store.Remove(handle); 118 return m_store.Remove(handle);
119 } 119 }
120 } 120 }
121 121
122 public void ClearExpiredAssociations() 122 public void ClearExpiredAssociations()
123 { 123 {
124 lock (m_syncRoot) 124 lock (m_syncRoot)
125 { 125 {
126 List<AssociationItem> itemsCopy = new List<AssociationItem>(m_sortedStore.Values); 126 List<AssociationItem> itemsCopy = new List<AssociationItem>(m_sortedStore.Values);
127 DateTime now = DateTime.Now; 127 DateTime now = DateTime.Now;
128 128
129 for (int i = 0; i < itemsCopy.Count; i++) 129 for (int i = 0; i < itemsCopy.Count; i++)
130 { 130 {
131 AssociationItem item = itemsCopy[i]; 131 AssociationItem item = itemsCopy[i];
132 132
133 if (item.Expires <= now) 133 if (item.Expires <= now)
134 { 134 {
135 m_sortedStore.RemoveAt(i); 135 m_sortedStore.RemoveAt(i);
136 m_store.Remove(item.Handle); 136 m_store.Remove(item.Handle);
137 } 137 }
138 } 138 }
139 } 139 }
140 } 140 }
141 141
142 #endregion 142 #endregion
143 } 143 }
144 144
145 public class OpenIdStreamHandler : IStreamHandler 145 public class OpenIdStreamHandler : IStreamHandler
146 { 146 {
147 #region HTML 147 #region HTML
148 148
149 /// <summary>Login form used to authenticate OpenID requests</summary> 149 /// <summary>Login form used to authenticate OpenID requests</summary>
150 const string LOGIN_PAGE = 150 const string LOGIN_PAGE =
151@"<html> 151@"<html>
152<head><title>OpenSim OpenID Login</title></head> 152<head><title>OpenSim OpenID Login</title></head>
153<body> 153<body>
154<h3>OpenSim Login</h3> 154<h3>OpenSim Login</h3>
155<form method=""post""> 155<form method=""post"">
156<label for=""first"">First Name:</label> <input readonly type=""text"" name=""first"" id=""first"" value=""{0}""/> 156<label for=""first"">First Name:</label> <input readonly type=""text"" name=""first"" id=""first"" value=""{0}""/>
157<label for=""last"">Last Name:</label> <input readonly type=""text"" name=""last"" id=""last"" value=""{1}""/> 157<label for=""last"">Last Name:</label> <input readonly type=""text"" name=""last"" id=""last"" value=""{1}""/>
158<label for=""pass"">Password:</label> <input type=""password"" name=""pass"" id=""pass""/> 158<label for=""pass"">Password:</label> <input type=""password"" name=""pass"" id=""pass""/>
159<input type=""submit"" value=""Login""> 159<input type=""submit"" value=""Login"">
160</form> 160</form>
161</body> 161</body>
162</html>"; 162</html>";
163 163
164 /// <summary>Page shown for a valid OpenID identity</summary> 164 /// <summary>Page shown for a valid OpenID identity</summary>
165 const string OPENID_PAGE = 165 const string OPENID_PAGE =
166@"<html> 166@"<html>
167<head> 167<head>
168<title>{2} {3}</title> 168<title>{2} {3}</title>
169<link rel=""openid2.provider openid.server"" href=""{0}://{1}/openid/server/""/> 169<link rel=""openid2.provider openid.server"" href=""{0}://{1}/openid/server/""/>
170</head> 170</head>
171<body>OpenID identifier for {2} {3}</body> 171<body>OpenID identifier for {2} {3}</body>
172</html> 172</html>
173"; 173";
174 174
175 /// <summary>Page shown for an invalid OpenID identity</summary> 175 /// <summary>Page shown for an invalid OpenID identity</summary>
176 const string INVALID_OPENID_PAGE = 176 const string INVALID_OPENID_PAGE =
177@"<html><head><title>Identity not found</title></head> 177@"<html><head><title>Identity not found</title></head>
178<body>Invalid OpenID identity</body></html>"; 178<body>Invalid OpenID identity</body></html>";
179 179
180 /// <summary>Page shown if the OpenID endpoint is requested directly</summary> 180 /// <summary>Page shown if the OpenID endpoint is requested directly</summary>
181 const string ENDPOINT_PAGE = 181 const string ENDPOINT_PAGE =
182@"<html><head><title>OpenID Endpoint</title></head><body> 182@"<html><head><title>OpenID Endpoint</title></head><body>
183This is an OpenID server endpoint, not a human-readable resource. 183This is an OpenID server endpoint, not a human-readable resource.
184For more information, see <a href='http://openid.net/'>http://openid.net/</a>. 184For more information, see <a href='http://openid.net/'>http://openid.net/</a>.
185</body></html>"; 185</body></html>";
186 186
187 #endregion HTML 187 #endregion HTML
188 188
189 public string ContentType { get { return m_contentType; } } 189 public string ContentType { get { return m_contentType; } }
190 public string HttpMethod { get { return m_httpMethod; } } 190 public string HttpMethod { get { return m_httpMethod; } }
191 public string Path { get { return m_path; } } 191 public string Path { get { return m_path; } }
192 192
193 string m_contentType; 193 string m_contentType;
194 string m_httpMethod; 194 string m_httpMethod;
195 string m_path; 195 string m_path;
196 UserLoginService m_loginService; 196 UserLoginService m_loginService;
197 ProviderMemoryStore m_openidStore = new ProviderMemoryStore(); 197 ProviderMemoryStore m_openidStore = new ProviderMemoryStore();
198 198
199 /// <summary> 199 /// <summary>
200 /// Constructor 200 /// Constructor
201 /// </summary> 201 /// </summary>
202 public OpenIdStreamHandler(string httpMethod, string path, UserLoginService loginService) 202 public OpenIdStreamHandler(string httpMethod, string path, UserLoginService loginService)
203 { 203 {
204 m_loginService = loginService; 204 m_loginService = loginService;
205 m_httpMethod = httpMethod; 205 m_httpMethod = httpMethod;
206 m_path = path; 206 m_path = path;
207 207
208 m_contentType = "text/html"; 208 m_contentType = "text/html";
209 } 209 }
210 210
211 /// <summary> 211 /// <summary>
212 /// Handles all GET and POST requests for OpenID identifier pages and endpoint 212 /// Handles all GET and POST requests for OpenID identifier pages and endpoint
213 /// server communication 213 /// server communication
214 /// </summary> 214 /// </summary>
215 public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse) 215 public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
216 { 216 {
217 Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath)); 217 Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath));
218 218
219 // Defult to returning HTML content 219 // Defult to returning HTML content
220 m_contentType = "text/html"; 220 m_contentType = "text/html";
221 221
222 try 222 try
223 { 223 {
224 NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd()); 224 NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd());
225 NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query); 225 NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query);
226 NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery); 226 NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery);
227 227
228 OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery); 228 OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery);
229 229
230 if (provider.Request != null) 230 if (provider.Request != null)
231 { 231 {
232 if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest) 232 if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest)
233 { 233 {
234 IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request; 234 IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request;
235 string[] passwordValues = postQuery.GetValues("pass"); 235 string[] passwordValues = postQuery.GetValues("pass");
236 236
237 UserProfileData profile; 237 UserProfileData profile;
238 if (TryGetProfile(new Uri(authRequest.ClaimedIdentifier.ToString()), out profile)) 238 if (TryGetProfile(new Uri(authRequest.ClaimedIdentifier.ToString()), out profile))
239 { 239 {
240 // Check for form POST data 240 // Check for form POST data
241 if (passwordValues != null && passwordValues.Length == 1) 241 if (passwordValues != null && passwordValues.Length == 1)
242 { 242 {
243 if (profile != null && m_loginService.AuthenticateUser(profile, passwordValues[0])) 243 if (profile != null && m_loginService.AuthenticateUser(profile, passwordValues[0]))
244 authRequest.IsAuthenticated = true; 244 authRequest.IsAuthenticated = true;
245 else 245 else
246 authRequest.IsAuthenticated = false; 246 authRequest.IsAuthenticated = false;
247 } 247 }
248 else 248 else
249 { 249 {
250 // Authentication was requested, send the client a login form 250 // Authentication was requested, send the client a login form
251 using (StreamWriter writer = new StreamWriter(response)) 251 using (StreamWriter writer = new StreamWriter(response))
252 writer.Write(String.Format(LOGIN_PAGE, profile.FirstName, profile.SurName)); 252 writer.Write(String.Format(LOGIN_PAGE, profile.FirstName, profile.SurName));
253 return; 253 return;
254 } 254 }
255 } 255 }
256 else 256 else
257 { 257 {
258 // Cannot find an avatar matching the claimed identifier 258 // Cannot find an avatar matching the claimed identifier
259 authRequest.IsAuthenticated = false; 259 authRequest.IsAuthenticated = false;
260 } 260 }
261 } 261 }
262 262
263 // Add OpenID headers to the response 263 // Add OpenID headers to the response
264 foreach (string key in provider.Request.Response.Headers.Keys) 264 foreach (string key in provider.Request.Response.Headers.Keys)
265 httpResponse.AddHeader(key, provider.Request.Response.Headers[key]); 265 httpResponse.AddHeader(key, provider.Request.Response.Headers[key]);
266 266
267 string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type"); 267 string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type");
268 if (contentTypeValues != null && contentTypeValues.Length == 1) 268 if (contentTypeValues != null && contentTypeValues.Length == 1)
269 m_contentType = contentTypeValues[0]; 269 m_contentType = contentTypeValues[0];
270 270
271 // Set the response code and document body based on the OpenID result 271 // Set the response code and document body based on the OpenID result
272 httpResponse.StatusCode = (int)provider.Request.Response.Code; 272 httpResponse.StatusCode = (int)provider.Request.Response.Code;
273 response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length); 273 response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length);
274 response.Close(); 274 response.Close();
275 } 275 }
276 else if (httpRequest.Url.AbsolutePath.Contains("/openid/server")) 276 else if (httpRequest.Url.AbsolutePath.Contains("/openid/server"))
277 { 277 {
278 // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page 278 // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page
279 using (StreamWriter writer = new StreamWriter(response)) 279 using (StreamWriter writer = new StreamWriter(response))
280 writer.Write(ENDPOINT_PAGE); 280 writer.Write(ENDPOINT_PAGE);
281 } 281 }
282 else 282 else
283 { 283 {
284 // Try and lookup this avatar 284 // Try and lookup this avatar
285 UserProfileData profile; 285 UserProfileData profile;
286 if (TryGetProfile(httpRequest.Url, out profile)) 286 if (TryGetProfile(httpRequest.Url, out profile))
287 { 287 {
288 using (StreamWriter writer = new StreamWriter(response)) 288 using (StreamWriter writer = new StreamWriter(response))
289 { 289 {
290 // TODO: Print out a full profile page for this avatar 290 // TODO: Print out a full profile page for this avatar
291 writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme, 291 writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme,
292 httpRequest.Url.Authority, profile.FirstName, profile.SurName)); 292 httpRequest.Url.Authority, profile.FirstName, profile.SurName));
293 } 293 }
294 } 294 }
295 else 295 else
296 { 296 {
297 // Couldn't parse an avatar name, or couldn't find the avatar in the user server 297 // Couldn't parse an avatar name, or couldn't find the avatar in the user server
298 using (StreamWriter writer = new StreamWriter(response)) 298 using (StreamWriter writer = new StreamWriter(response))
299 writer.Write(INVALID_OPENID_PAGE); 299 writer.Write(INVALID_OPENID_PAGE);
300 } 300 }
301 } 301 }
302 } 302 }
303 catch (Exception ex) 303 catch (Exception ex)
304 { 304 {
305 httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError; 305 httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
306 using (StreamWriter writer = new StreamWriter(response)) 306 using (StreamWriter writer = new StreamWriter(response))
307 writer.Write(ex.Message); 307 writer.Write(ex.Message);
308 } 308 }
309 } 309 }
310 310
311 /// <summary> 311 /// <summary>
312 /// Parse a URL with a relative path of the form /users/First_Last and try to 312 /// Parse a URL with a relative path of the form /users/First_Last and try to
313 /// retrieve the profile matching that avatar name 313 /// retrieve the profile matching that avatar name
314 /// </summary> 314 /// </summary>
315 /// <param name="requestUrl">URL to parse for an avatar name</param> 315 /// <param name="requestUrl">URL to parse for an avatar name</param>
316 /// <param name="profile">Profile data for the avatar</param> 316 /// <param name="profile">Profile data for the avatar</param>
317 /// <returns>True if the parse and lookup were successful, otherwise false</returns> 317 /// <returns>True if the parse and lookup were successful, otherwise false</returns>
318 bool TryGetProfile(Uri requestUrl, out UserProfileData profile) 318 bool TryGetProfile(Uri requestUrl, out UserProfileData profile)
319 { 319 {
320 if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/") 320 if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/")
321 { 321 {
322 // Parse the avatar name from the path 322 // Parse the avatar name from the path
323 string username = requestUrl.Segments[requestUrl.Segments.Length - 1]; 323 string username = requestUrl.Segments[requestUrl.Segments.Length - 1];
324 string[] name = username.Split('_'); 324 string[] name = username.Split('_');
325 325
326 if (name.Length == 2) 326 if (name.Length == 2)
327 { 327 {
328 profile = m_loginService.GetTheUser(name[0], name[1]); 328 profile = m_loginService.GetTheUser(name[0], name[1]);
329 return (profile != null); 329 return (profile != null);
330 } 330 }
331 } 331 }
332 332
333 profile = null; 333 profile = null;
334 return false; 334 return false;
335 } 335 }
336 } 336 }
337} 337}