Account credential retrieval changed to use TcpListener instead of HttpListener....
authorPanagiotis Kanavos <pkanavos@gmail.com>
Fri, 2 Dec 2011 20:26:16 +0000 (22:26 +0200)
committerPanagiotis Kanavos <pkanavos@gmail.com>
Fri, 2 Dec 2011 20:26:16 +0000 (22:26 +0200)
trunk/Pithos.Client.WPF/PithosAccount.cs
trunk/Pithos.Client.WPF/Preferences/AddAccountViewModel.cs
trunk/Pithos.Client.WPF/Preferences/PreferencesViewModel.cs
trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs

index d4f3e73..69e9be9 100644 (file)
@@ -4,6 +4,7 @@
 // </copyright>
 // -----------------------------------------------------------------------
 
+using System.IO;
 using Pithos.Network;
 using log4net;
 
@@ -34,7 +35,7 @@ namespace Pithos.Client.WPF
         /// </summary>
         /// <param name="loginUrl">URL to retrieve the account info from PITHOS. Must end with =</param>
         /// <returns>The credentials wrapped in a Task</returns>
-        public static Task<NetworkCredential> RetrieveCredentialsAsync(string loginUrl)
+        public static Task<NetworkCredential> RetrieveCredentials(string loginUrl)
         {
             Contract.Requires(Uri.IsWellFormedUriString(loginUrl, UriKind.Absolute));
 
@@ -46,68 +47,119 @@ namespace Pithos.Client.WPF
 
             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);
+
+            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<NetworkCredential> ListenHttpForRedirectAsync(string listenerUrl)
+        {
+            using (var listener = new HttpListener())
+            {
+                listener.Prefixes.Add(listenerUrl);
+
+                Log.InfoFormat("[RETRIEVE] Listening at {0}", listenerUrl);
 
-            listener.Start();
-            
-            var startListening = Task.Factory.FromAsync<HttpListenerContext>(listener.BeginGetContext, listener.EndGetContext, null)
-                .WithTimeout(TimeSpan.FromMinutes(5));
 
-            var receiveCredentials=startListening.ContinueWith(tc =>
+                try
                 {
-                    try
-                    {
-                        if (tc.IsFaulted)
-                        {
-                            Log.Error("[RETRIEVE][ERROR] Receive connection {0}", tc.Exception);
-                            throw tc.Exception;
-                        }
-                        else
-                        {
+                    listener.Start();
 
-                            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"];
-                            if (String.IsNullOrWhiteSpace(userName) || String.IsNullOrWhiteSpace(token))
-                            {
-                                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);
-                            }
-                        }
+                    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))
+                    {
+                        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");
                     }
-                    finally
+                    else
                     {
-                        listener.Close();
+                        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;
+                }
+            }
+        }
 
-            var uriBuilder=new UriBuilder(loginUrl);                       
-            uriBuilder.Query="next=" + listenerUrl;
+        private static async Task<NetworkCredential> ListenForRedirectAsync(int port)
+        {
+            var listener = new TcpListener(IPAddress.Any, port);            
+            Log.InfoFormat("[RETRIEVE] Listening at {0}", port);
 
-            var retrieveUri = uriBuilder.Uri;
-            Log.InfoFormat("[RETRIEVE] Open Browser at {0}", retrieveUri);
-            Process.Start(retrieveUri.ToString());
+            try
+            {
+                listener.Start();
 
-            return receiveCredentials;
+                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();
+                        Log.InfoFormat("[RETRIEVE] Got Connection {0}", request);
+
+
+                        var parts=request.Split(' ');
+                        var query = parts[1].TrimStart('/','?');
+
+                        var items = query.Split('&')
+                            .Select(pair => pair.Split('='))
+                            .ToDictionary(arr => arr[0].ToLower(), arr => Uri.UnescapeDataString(arr[1]));
+
+                        var userName = items["user"];
+                        var token = items["token"];
+                        if (String.IsNullOrWhiteSpace(userName) ||
+                            String.IsNullOrWhiteSpace(token))
+                        {
+                            Respond(stream, "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(stream, "Authenticated", "Got It");                            
+                        }
+                        return new NetworkCredential(userName, token);
+                    }
+                }
+            }
+            catch (Exception exc)
+            {
+                Log.Error("[RETRIEVE][ERROR] Receive connection {0}", exc);
+                throw;
+            }
         }
 
+
+
         private static void Respond(HttpListenerContext context,string title,string message)
         {
             var response = context.Response;
@@ -121,6 +173,27 @@ namespace Pithos.Client.WPF
 
             Log.InfoFormat("[RETRIEVE] Responded");
         }
+
+        private static void Respond(Stream stream,string title,string message)
+        {
+            
+            var html = String.Format("<html><head><title>{0}</title></head><body><h1>{1}</h1></body></html>", 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");
+        }
+
+       
         /// <summary>
         /// Locates a free local port 
         /// </summary>
index 778ea0d..d835cf4 100644 (file)
@@ -124,7 +124,7 @@ namespace Pithos.Client.WPF.Preferences
 
             try
             {
-                var credentials = await PithosAccount.RetrieveCredentialsAsync(Settings.Default.PithosLoginUrl);
+                var credentials = await PithosAccount.RetrieveCredentials(Settings.Default.PithosLoginUrl);
                 AccountName = credentials.UserName;
                 Token = credentials.Password;
 
index be08cc9..a923730 100644 (file)
@@ -250,7 +250,7 @@ namespace Pithos.Client.WPF
 
         public async void AddPithosAccount()
        {
-            var credentials=await PithosAccount.RetrieveCredentialsAsync(Settings.PithosLoginUrl);
+            var credentials=await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
             var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
             if (account == null)
             {
index 570450a..2099e38 100644 (file)
@@ -523,7 +523,7 @@ namespace Pithos.Client.WPF {
             try
             {
 
-                var credentials = await PithosAccount.RetrieveCredentialsAsync(Settings.PithosLoginUrl);
+                var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
 
                 var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
                 account.ApiKey = credentials.Password;