aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/UserServer/OpenIdService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Grid/UserServer/OpenIdService.cs')
-rw-r--r--OpenSim/Grid/UserServer/OpenIdService.cs678
1 files changed, 339 insertions, 339 deletions
diff --git a/OpenSim/Grid/UserServer/OpenIdService.cs b/OpenSim/Grid/UserServer/OpenIdService.cs
index 695968f..78e820e 100644
--- a/OpenSim/Grid/UserServer/OpenIdService.cs
+++ b/OpenSim/Grid/UserServer/OpenIdService.cs
@@ -1,339 +1,339 @@
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 System.Text; 34using System.Text;
35using DotNetOpenId; 35using DotNetOpenId;
36using DotNetOpenId.Provider; 36using DotNetOpenId.Provider;
37using log4net; 37using log4net;
38using OpenSim.Framework; 38using OpenSim.Framework;
39using OpenSim.Framework.Servers; 39using OpenSim.Framework.Servers;
40 40
41namespace OpenSim.Grid.UserServer 41namespace OpenSim.Grid.UserServer
42{ 42{
43 /// <summary> 43 /// <summary>
44 /// Temporary, in-memory store for OpenID associations 44 /// Temporary, in-memory store for OpenID associations
45 /// </summary> 45 /// </summary>
46 public class ProviderMemoryStore : IAssociationStore<AssociationRelyingPartyType> 46 public class ProviderMemoryStore : IAssociationStore<AssociationRelyingPartyType>
47 { 47 {
48 private class AssociationItem 48 private class AssociationItem
49 { 49 {
50 public AssociationRelyingPartyType DistinguishingFactor; 50 public AssociationRelyingPartyType DistinguishingFactor;
51 public string Handle; 51 public string Handle;
52 public DateTime Expires; 52 public DateTime Expires;
53 public byte[] PrivateData; 53 public byte[] PrivateData;
54 } 54 }
55 55
56 Dictionary<string, AssociationItem> m_store = new Dictionary<string, AssociationItem>(); 56 Dictionary<string, AssociationItem> m_store = new Dictionary<string, AssociationItem>();
57 SortedList<DateTime, AssociationItem> m_sortedStore = new SortedList<DateTime, AssociationItem>(); 57 SortedList<DateTime, AssociationItem> m_sortedStore = new SortedList<DateTime, AssociationItem>();
58 object m_syncRoot = new object(); 58 object m_syncRoot = new object();
59 59
60 #region IAssociationStore<AssociationRelyingPartyType> Members 60 #region IAssociationStore<AssociationRelyingPartyType> Members
61 61
62 public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc) 62 public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc)
63 { 63 {
64 AssociationItem item = new AssociationItem(); 64 AssociationItem item = new AssociationItem();
65 item.DistinguishingFactor = distinguishingFactor; 65 item.DistinguishingFactor = distinguishingFactor;
66 item.Handle = assoc.Handle; 66 item.Handle = assoc.Handle;
67 item.Expires = assoc.Expires.ToLocalTime(); 67 item.Expires = assoc.Expires.ToLocalTime();
68 item.PrivateData = assoc.SerializePrivateData(); 68 item.PrivateData = assoc.SerializePrivateData();
69 69
70 lock (m_syncRoot) 70 lock (m_syncRoot)
71 { 71 {
72 m_store[item.Handle] = item; 72 m_store[item.Handle] = item;
73 m_sortedStore[item.Expires] = item; 73 m_sortedStore[item.Expires] = item;
74 } 74 }
75 } 75 }
76 76
77 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor) 77 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor)
78 { 78 {
79 lock (m_syncRoot) 79 lock (m_syncRoot)
80 { 80 {
81 if (m_sortedStore.Count > 0) 81 if (m_sortedStore.Count > 0)
82 { 82 {
83 AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1]; 83 AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1];
84 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData); 84 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
85 } 85 }
86 else 86 else
87 { 87 {
88 return null; 88 return null;
89 } 89 }
90 } 90 }
91 } 91 }
92 92
93 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle) 93 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
94 { 94 {
95 AssociationItem item; 95 AssociationItem item;
96 bool success = false; 96 bool success = false;
97 lock (m_syncRoot) 97 lock (m_syncRoot)
98 success = m_store.TryGetValue(handle, out item); 98 success = m_store.TryGetValue(handle, out item);
99 99
100 if (success) 100 if (success)
101 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData); 101 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
102 else 102 else
103 return null; 103 return null;
104 } 104 }
105 105
106 public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle) 106 public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
107 { 107 {
108 lock (m_syncRoot) 108 lock (m_syncRoot)
109 { 109 {
110 for (int i = 0; i < m_sortedStore.Values.Count; i++) 110 for (int i = 0; i < m_sortedStore.Values.Count; i++)
111 { 111 {
112 AssociationItem item = m_sortedStore.Values[i]; 112 AssociationItem item = m_sortedStore.Values[i];
113 if (item.Handle == handle) 113 if (item.Handle == handle)
114 { 114 {
115 m_sortedStore.RemoveAt(i); 115 m_sortedStore.RemoveAt(i);
116 break; 116 break;
117 } 117 }
118 } 118 }
119 119
120 return m_store.Remove(handle); 120 return m_store.Remove(handle);
121 } 121 }
122 } 122 }
123 123
124 public void ClearExpiredAssociations() 124 public void ClearExpiredAssociations()
125 { 125 {
126 lock (m_syncRoot) 126 lock (m_syncRoot)
127 { 127 {
128 List<AssociationItem> itemsCopy = new List<AssociationItem>(m_sortedStore.Values); 128 List<AssociationItem> itemsCopy = new List<AssociationItem>(m_sortedStore.Values);
129 DateTime now = DateTime.Now; 129 DateTime now = DateTime.Now;
130 130
131 for (int i = 0; i < itemsCopy.Count; i++) 131 for (int i = 0; i < itemsCopy.Count; i++)
132 { 132 {
133 AssociationItem item = itemsCopy[i]; 133 AssociationItem item = itemsCopy[i];
134 134
135 if (item.Expires <= now) 135 if (item.Expires <= now)
136 { 136 {
137 m_sortedStore.RemoveAt(i); 137 m_sortedStore.RemoveAt(i);
138 m_store.Remove(item.Handle); 138 m_store.Remove(item.Handle);
139 } 139 }
140 } 140 }
141 } 141 }
142 } 142 }
143 143
144 #endregion 144 #endregion
145 } 145 }
146 146
147 public class OpenIdStreamHandler : IStreamHandler 147 public class OpenIdStreamHandler : IStreamHandler
148 { 148 {
149 #region HTML 149 #region HTML
150 150
151 /// <summary>Login form used to authenticate OpenID requests</summary> 151 /// <summary>Login form used to authenticate OpenID requests</summary>
152 const string LOGIN_PAGE = 152 const string LOGIN_PAGE =
153@"<html> 153@"<html>
154<head><title>OpenSim OpenID Login</title></head> 154<head><title>OpenSim OpenID Login</title></head>
155<body> 155<body>
156<h3>OpenSim Login</h3> 156<h3>OpenSim Login</h3>
157<form method=""post""> 157<form method=""post"">
158<label for=""first"">First Name:</label> <input readonly type=""text"" name=""first"" id=""first"" value=""{0}""/> 158<label for=""first"">First Name:</label> <input readonly type=""text"" name=""first"" id=""first"" value=""{0}""/>
159<label for=""last"">Last Name:</label> <input readonly type=""text"" name=""last"" id=""last"" value=""{1}""/> 159<label for=""last"">Last Name:</label> <input readonly type=""text"" name=""last"" id=""last"" value=""{1}""/>
160<label for=""pass"">Password:</label> <input type=""password"" name=""pass"" id=""pass""/> 160<label for=""pass"">Password:</label> <input type=""password"" name=""pass"" id=""pass""/>
161<input type=""submit"" value=""Login""> 161<input type=""submit"" value=""Login"">
162</form> 162</form>
163</body> 163</body>
164</html>"; 164</html>";
165 165
166 /// <summary>Page shown for a valid OpenID identity</summary> 166 /// <summary>Page shown for a valid OpenID identity</summary>
167 const string OPENID_PAGE = 167 const string OPENID_PAGE =
168@"<html> 168@"<html>
169<head> 169<head>
170<title>{2} {3}</title> 170<title>{2} {3}</title>
171<link rel=""openid2.provider openid.server"" href=""{0}://{1}/openid/server/""/> 171<link rel=""openid2.provider openid.server"" href=""{0}://{1}/openid/server/""/>
172</head> 172</head>
173<body>OpenID identifier for {2} {3}</body> 173<body>OpenID identifier for {2} {3}</body>
174</html> 174</html>
175"; 175";
176 176
177 /// <summary>Page shown for an invalid OpenID identity</summary> 177 /// <summary>Page shown for an invalid OpenID identity</summary>
178 const string INVALID_OPENID_PAGE = 178 const string INVALID_OPENID_PAGE =
179@"<html><head><title>Identity not found</title></head> 179@"<html><head><title>Identity not found</title></head>
180<body>Invalid OpenID identity</body></html>"; 180<body>Invalid OpenID identity</body></html>";
181 181
182 /// <summary>Page shown if the OpenID endpoint is requested directly</summary> 182 /// <summary>Page shown if the OpenID endpoint is requested directly</summary>
183 const string ENDPOINT_PAGE = 183 const string ENDPOINT_PAGE =
184@"<html><head><title>OpenID Endpoint</title></head><body> 184@"<html><head><title>OpenID Endpoint</title></head><body>
185This is an OpenID server endpoint, not a human-readable resource. 185This is an OpenID server endpoint, not a human-readable resource.
186For more information, see <a href='http://openid.net/'>http://openid.net/</a>. 186For more information, see <a href='http://openid.net/'>http://openid.net/</a>.
187</body></html>"; 187</body></html>";
188 188
189 #endregion HTML 189 #endregion HTML
190 190
191 public string ContentType { get { return m_contentType; } } 191 public string ContentType { get { return m_contentType; } }
192 public string HttpMethod { get { return m_httpMethod; } } 192 public string HttpMethod { get { return m_httpMethod; } }
193 public string Path { get { return m_path; } } 193 public string Path { get { return m_path; } }
194 194
195 string m_contentType; 195 string m_contentType;
196 string m_httpMethod; 196 string m_httpMethod;
197 string m_path; 197 string m_path;
198 UserLoginService m_loginService; 198 UserLoginService m_loginService;
199 ProviderMemoryStore m_openidStore = new ProviderMemoryStore(); 199 ProviderMemoryStore m_openidStore = new ProviderMemoryStore();
200 200
201 /// <summary> 201 /// <summary>
202 /// Constructor 202 /// Constructor
203 /// </summary> 203 /// </summary>
204 public OpenIdStreamHandler(string httpMethod, string path, UserLoginService loginService) 204 public OpenIdStreamHandler(string httpMethod, string path, UserLoginService loginService)
205 { 205 {
206 m_loginService = loginService; 206 m_loginService = loginService;
207 m_httpMethod = httpMethod; 207 m_httpMethod = httpMethod;
208 m_path = path; 208 m_path = path;
209 209
210 m_contentType = "text/html"; 210 m_contentType = "text/html";
211 } 211 }
212 212
213 /// <summary> 213 /// <summary>
214 /// Handles all GET and POST requests for OpenID identifier pages and endpoint 214 /// Handles all GET and POST requests for OpenID identifier pages and endpoint
215 /// server communication 215 /// server communication
216 /// </summary> 216 /// </summary>
217 public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse) 217 public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
218 { 218 {
219 Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath)); 219 Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath));
220 220
221 // Defult to returning HTML content 221 // Defult to returning HTML content
222 m_contentType = "text/html"; 222 m_contentType = "text/html";
223 223
224 try 224 try
225 { 225 {
226 NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd()); 226 NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd());
227 NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query); 227 NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query);
228 NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery); 228 NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery);
229 229
230 OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery); 230 OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery);
231 231
232 if (provider.Request != null) 232 if (provider.Request != null)
233 { 233 {
234 if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest) 234 if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest)
235 { 235 {
236 IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request; 236 IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request;
237 string[] passwordValues = postQuery.GetValues("pass"); 237 string[] passwordValues = postQuery.GetValues("pass");
238 238
239 UserProfileData profile; 239 UserProfileData profile;
240 if (TryGetProfile(new Uri(authRequest.ClaimedIdentifier.ToString()), out profile)) 240 if (TryGetProfile(new Uri(authRequest.ClaimedIdentifier.ToString()), out profile))
241 { 241 {
242 // Check for form POST data 242 // Check for form POST data
243 if (passwordValues != null && passwordValues.Length == 1) 243 if (passwordValues != null && passwordValues.Length == 1)
244 { 244 {
245 if (profile != null && m_loginService.AuthenticateUser(profile, passwordValues[0])) 245 if (profile != null && m_loginService.AuthenticateUser(profile, passwordValues[0]))
246 authRequest.IsAuthenticated = true; 246 authRequest.IsAuthenticated = true;
247 else 247 else
248 authRequest.IsAuthenticated = false; 248 authRequest.IsAuthenticated = false;
249 } 249 }
250 else 250 else
251 { 251 {
252 // Authentication was requested, send the client a login form 252 // Authentication was requested, send the client a login form
253 using (StreamWriter writer = new StreamWriter(response)) 253 using (StreamWriter writer = new StreamWriter(response))
254 writer.Write(String.Format(LOGIN_PAGE, profile.FirstName, profile.SurName)); 254 writer.Write(String.Format(LOGIN_PAGE, profile.FirstName, profile.SurName));
255 return; 255 return;
256 } 256 }
257 } 257 }
258 else 258 else
259 { 259 {
260 // Cannot find an avatar matching the claimed identifier 260 // Cannot find an avatar matching the claimed identifier
261 authRequest.IsAuthenticated = false; 261 authRequest.IsAuthenticated = false;
262 } 262 }
263 } 263 }
264 264
265 // Add OpenID headers to the response 265 // Add OpenID headers to the response
266 foreach (string key in provider.Request.Response.Headers.Keys) 266 foreach (string key in provider.Request.Response.Headers.Keys)
267 httpResponse.AddHeader(key, provider.Request.Response.Headers[key]); 267 httpResponse.AddHeader(key, provider.Request.Response.Headers[key]);
268 268
269 string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type"); 269 string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type");
270 if (contentTypeValues != null && contentTypeValues.Length == 1) 270 if (contentTypeValues != null && contentTypeValues.Length == 1)
271 m_contentType = contentTypeValues[0]; 271 m_contentType = contentTypeValues[0];
272 272
273 // Set the response code and document body based on the OpenID result 273 // Set the response code and document body based on the OpenID result
274 httpResponse.StatusCode = (int)provider.Request.Response.Code; 274 httpResponse.StatusCode = (int)provider.Request.Response.Code;
275 response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length); 275 response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length);
276 response.Close(); 276 response.Close();
277 } 277 }
278 else if (httpRequest.Url.AbsolutePath.Contains("/openid/server")) 278 else if (httpRequest.Url.AbsolutePath.Contains("/openid/server"))
279 { 279 {
280 // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page 280 // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page
281 using (StreamWriter writer = new StreamWriter(response)) 281 using (StreamWriter writer = new StreamWriter(response))
282 writer.Write(ENDPOINT_PAGE); 282 writer.Write(ENDPOINT_PAGE);
283 } 283 }
284 else 284 else
285 { 285 {
286 // Try and lookup this avatar 286 // Try and lookup this avatar
287 UserProfileData profile; 287 UserProfileData profile;
288 if (TryGetProfile(httpRequest.Url, out profile)) 288 if (TryGetProfile(httpRequest.Url, out profile))
289 { 289 {
290 using (StreamWriter writer = new StreamWriter(response)) 290 using (StreamWriter writer = new StreamWriter(response))
291 { 291 {
292 // TODO: Print out a full profile page for this avatar 292 // TODO: Print out a full profile page for this avatar
293 writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme, 293 writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme,
294 httpRequest.Url.Authority, profile.FirstName, profile.SurName)); 294 httpRequest.Url.Authority, profile.FirstName, profile.SurName));
295 } 295 }
296 } 296 }
297 else 297 else
298 { 298 {
299 // Couldn't parse an avatar name, or couldn't find the avatar in the user server 299 // Couldn't parse an avatar name, or couldn't find the avatar in the user server
300 using (StreamWriter writer = new StreamWriter(response)) 300 using (StreamWriter writer = new StreamWriter(response))
301 writer.Write(INVALID_OPENID_PAGE); 301 writer.Write(INVALID_OPENID_PAGE);
302 } 302 }
303 } 303 }
304 } 304 }
305 catch (Exception ex) 305 catch (Exception ex)
306 { 306 {
307 httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError; 307 httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
308 using (StreamWriter writer = new StreamWriter(response)) 308 using (StreamWriter writer = new StreamWriter(response))
309 writer.Write(ex.Message); 309 writer.Write(ex.Message);
310 } 310 }
311 } 311 }
312 312
313 /// <summary> 313 /// <summary>
314 /// Parse a URL with a relative path of the form /users/First_Last and try to 314 /// Parse a URL with a relative path of the form /users/First_Last and try to
315 /// retrieve the profile matching that avatar name 315 /// retrieve the profile matching that avatar name
316 /// </summary> 316 /// </summary>
317 /// <param name="requestUrl">URL to parse for an avatar name</param> 317 /// <param name="requestUrl">URL to parse for an avatar name</param>
318 /// <param name="profile">Profile data for the avatar</param> 318 /// <param name="profile">Profile data for the avatar</param>
319 /// <returns>True if the parse and lookup were successful, otherwise false</returns> 319 /// <returns>True if the parse and lookup were successful, otherwise false</returns>
320 bool TryGetProfile(Uri requestUrl, out UserProfileData profile) 320 bool TryGetProfile(Uri requestUrl, out UserProfileData profile)
321 { 321 {
322 if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/") 322 if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/")
323 { 323 {
324 // Parse the avatar name from the path 324 // Parse the avatar name from the path
325 string username = requestUrl.Segments[requestUrl.Segments.Length - 1]; 325 string username = requestUrl.Segments[requestUrl.Segments.Length - 1];
326 string[] name = username.Split('_'); 326 string[] name = username.Split('_');
327 327
328 if (name.Length == 2) 328 if (name.Length == 2)
329 { 329 {
330 profile = m_loginService.GetTheUser(name[0], name[1]); 330 profile = m_loginService.GetTheUser(name[0], name[1]);
331 return (profile != null); 331 return (profile != null);
332 } 332 }
333 } 333 }
334 334
335 profile = null; 335 profile = null;
336 return false; 336 return false;
337 } 337 }
338 } 338 }
339} 339}