X-Git-Url: https://code.grnet.gr/git/pithos-ms-client/blobdiff_plain/c28a075a5149de9d2d7fe8710bb3cf226f6bacc8..c636df1fbc0e7f6b1d1a0c33253848adc0d412e4:/trunk/Pithos.Client.WPF/PithosAccount.cs diff --git a/trunk/Pithos.Client.WPF/PithosAccount.cs b/trunk/Pithos.Client.WPF/PithosAccount.cs index ad22534..06d7963 100644 --- a/trunk/Pithos.Client.WPF/PithosAccount.cs +++ b/trunk/Pithos.Client.WPF/PithosAccount.cs @@ -1,9 +1,49 @@ -// ----------------------------------------------------------------------- -// -// TODO: Update copyright text. +#region +/* ----------------------------------------------------------------------- + * + * + * Copyright 2011-2012 GRNET S.A. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * + * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and + * documentation are those of the authors and should not be + * interpreted as representing official policies, either expressed + * or implied, of GRNET S.A. + * + * ----------------------------------------------------------------------- + */ +#endregion + // // ----------------------------------------------------------------------- +using System.IO; using Pithos.Network; using log4net; @@ -32,78 +72,155 @@ namespace Pithos.Client.WPF /// /// Asynchronously retrieves PITHOS credentials /// - /// URL to retrieve the account info from PITHOS. Must end with = + /// URL to retrieve the account info from PITHOS. Must end with = /// The credentials wrapped in a Task - public static Task RetrieveCredentialsAsync(string pithosSite) + public static Task RetrieveCredentials(string loginUrl) { - Contract.Requires(Uri.IsWellFormedUriString(pithosSite, UriKind.Absolute)); + Contract.Requires(Uri.IsWellFormedUriString(loginUrl, UriKind.Absolute)); - if (!Uri.IsWellFormedUriString(pithosSite, UriKind.Absolute)) - throw new ArgumentException("The pithosSite parameter must be a valid absolute URL", "pithosSite"); + if (!Uri.IsWellFormedUriString(loginUrl, UriKind.Absolute)) + throw new ArgumentException("The pithosSite parameter must be a valid absolute URL", "loginUrl"); int port = GetFreePort(); + + //TODO:Force logout here to take care of existing cookies var listenerUrl = String.Format("http://127.0.0.1:{0}/", port); - HttpListener listener = new HttpListener(); - listener.Prefixes.Add(listenerUrl); + - Log.InfoFormat("[RETRIEVE] Listening at {0}", listenerUrl); + var receiveCredentials = ListenForRedirectAsync(port); - listener.Start(); - - var startListening = Task.Factory.FromAsync(listener.BeginGetContext, listener.EndGetContext, null) - .WithTimeout(TimeSpan.FromMinutes(5)); + var logoutUrl = loginUrl.Replace("login", "im/logout"); + var logoutProcess=Process.Start(logoutUrl); + TaskEx.Delay(2000).Wait(); + var uriBuilder=new UriBuilder(loginUrl); + uriBuilder.Query="next=" + listenerUrl; + + var retrieveUri = uriBuilder.Uri; + Log.InfoFormat("[RETRIEVE] Open Browser at {0}", retrieveUri); + Process.Start(retrieveUri.ToString()); + + return receiveCredentials; + } + +/* + private static async Task ListenHttpForRedirectAsync(string listenerUrl) + { + using (var listener = new HttpListener()) + { + listener.Prefixes.Add(listenerUrl); + + Log.InfoFormat("[RETRIEVE] Listening at {0}", listenerUrl); - var receiveCredentials=startListening.ContinueWith(tc => + + try { - try + listener.Start(); + + var context = await listener.GetContextAsync() + .WithTimeout(TimeSpan.FromMinutes(5)); + + var request = context.Request; + Log.InfoFormat("[RETRIEVE] Got Connection {0}", request.RawUrl); + + var query = request.QueryString; + var userName = query["user"]; + var token = query["token"]; + if (String.IsNullOrWhiteSpace(userName) || + String.IsNullOrWhiteSpace(token)) { - if (tc.IsFaulted) + Respond(context, "Failure", "The server did not return a username or token"); + Log.ErrorFormat("[RETRIEVE] No credentials returned by server"); + throw new Exception("The server did not return a username or token"); + } + else + { + Log.InfoFormat("[RETRIEVE] Credentials retrieved user:{0} token:{1}", userName, token); + Respond(context, "Authenticated", "Got It"); + + return new NetworkCredential(userName, token); + } + } + catch (Exception exc) + { + Log.Error("[RETRIEVE][ERROR] Receive connection {0}", exc); + throw; + } + } + } +*/ + + private static async Task ListenForRedirectAsync(int port) + { + var listener = new TcpListener(IPAddress.Any, port); + Log.InfoFormat("[RETRIEVE] Listening at {0}", port); + + try + { + listener.Start(); + + using (var client = await listener.AcceptTcpClientAsync() + .WithTimeout(TimeSpan.FromMinutes(5))) + { + using (var stream = client.GetStream()) + using (var reader=new StreamReader(stream)) + { + var request = await reader.ReadLineAsync(); + //BUG + //TODO: Add proper warnings here if the content is empty, don't just throw an exception + //This may be a common occurence + + if (String.IsNullOrWhiteSpace(request)) + throw new PithosException("The server did send any information"); + Log.InfoFormat("[RETRIEVE] Got Connection {0}", request); + + + var items = ParseResponse(request); + + var userName = items["user"]; + var token = items["token"]; + if (String.IsNullOrWhiteSpace(userName) || + String.IsNullOrWhiteSpace(token)) { - Log.Error("[RETRIEVE][ERROR] Receive connection {0}", tc.Exception); - throw tc.Exception; + Respond(stream, "Failure", "The server did not return a username or token"); + Log.ErrorFormat("[RETRIEVE] No credentials returned by server"); + throw new PithosException("The server did not return a username or token"); } else { - - var context = tc.Result; - var request = context.Request; - Log.InfoFormat("[RETRIEVE] Got Connection {0}", request.RawUrl); - - var query = request.QueryString; - var userName = query["user"]; - var token = query["token"]; - Log.InfoFormat("[RETRIEVE] Credentials retrieved user:{0} token:{1}", userName, token); - Respond(context); - - return new NetworkCredential(userName, token); + Respond(stream, "Authenticated", "Got It"); } - + return new NetworkCredential(userName, token); } - finally - { - listener.Close(); - } - }); - - var uriBuilder=new UriBuilder(pithosSite); - uriBuilder.Path="login"; - uriBuilder.Query="next=" + listenerUrl; + } + } + catch (Exception exc) + { + Log.Error("[RETRIEVE][ERROR] Receive connection {0}", exc); + throw new PithosException("An error occured while retrieving credentials",exc); + } + } - var retrieveUri = uriBuilder.Uri; - Log.InfoFormat("[RETRIEVE] Open Browser at {0}", retrieveUri); - Process.Start(retrieveUri.ToString()); + private static Dictionary ParseResponse(string request) + { + var parts = request.Split(' '); + var query = parts[1].TrimStart('/', '?'); - return receiveCredentials; + var items = query.Split('&') + .Select(pair => pair.Split('=')) + .ToDictionary(arr => arr[0].ToLower(), arr => Uri.UnescapeDataString(arr[1])); + return items; } - private static void Respond(HttpListenerContext context) + + private static void Respond(HttpListenerContext context,string title,string message) { var response = context.Response; - var outBuffer = Encoding.UTF8.GetBytes("Authenticated

Got It

"); + var html = String.Format("{0}

{1}

", title, message); + var outBuffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = outBuffer.Length; using (var stream = response.OutputStream) { @@ -112,6 +229,27 @@ namespace Pithos.Client.WPF Log.InfoFormat("[RETRIEVE] Responded"); } + + private static void Respond(Stream stream,string title,string message) + { + + var html = String.Format("{0}

{1}

", title, message); + + var response = new StringBuilder(); + response.AppendLine("HTTP/1.1 200 OK"); + response.AppendFormat("Content-Length: {0}\n", html.Length); + response.AppendLine("Server: Microsoft-HTTPAPI/2.0"); + response.AppendFormat("Date: {0}\n\n", DateTime.UtcNow); + response.AppendLine(html); + + var outBuffer=Encoding.UTF8.GetBytes(response.ToString()); + + stream.Write(outBuffer, 0, outBuffer.Length); + + Log.InfoFormat("[RETRIEVE] Responded"); + } + + /// /// Locates a free local port ///