-// -----------------------------------------------------------------------
-// <copyright file="PithosAccount.cs" company="Microsoft">
-// TODO: Update copyright text.
+#region
+/* -----------------------------------------------------------------------
+ * <copyright file="PithosAccount.cs" company="GRNet">
+ *
+ * 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.
+ * </copyright>
+ * -----------------------------------------------------------------------
+ */
+#endregion
+
// </copyright>
// -----------------------------------------------------------------------
+using System.IO;
+using System.Reflection;
+using Pithos.Client.WPF.Preferences;
using Pithos.Network;
using log4net;
/// </summary>
public static class PithosAccount
{
- private static readonly ILog Log = LogManager.GetLogger(typeof(PithosAccount));
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Asynchronously retrieves PITHOS credentials
/// </summary>
- /// <param name="pithosSite">URL to retrieve the account info from PITHOS. Must end with =</param>
+ /// <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 pithosSite)
+ public static NetworkCredential RetrieveCredentials(string loginUrl,string accountName=null)
{
- 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();
- int port = GetFreePort();
+ //TODO:Force logout here to take care of existing cookies
- var listenerUrl = String.Format("http://127.0.0.1:{0}/", port);
+ //var listenerUrl = String.Format("http://127.0.0.1:{0}", port);
+ var listenerUrl = "pithos://127.0.0.1";
- 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<HttpListenerContext>(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)
+ {
+ Query = String.Format("next={0}&force=", listenerUrl)
+ };
+
+ var retrieveUri = uriBuilder.Uri;
+
+
+ var browser = new LoginView(retrieveUri,accountName);
+ if (true == browser.ShowDialog())
+ {
+ return new NetworkCredential(browser.Account, browser.Token);
+ }
+ return null;
+
+/*
+
+ Log.InfoFormat("[RETRIEVE] Open Browser at {0}", retrieveUri);
+ Process.Start(retrieveUri.ToString());
+
+ return receiveCredentials;
+*/
+ }
- var receiveCredentials=startListening.ContinueWith(tc =>
+/*
+ private static async Task<NetworkCredential> ListenHttpForRedirectAsync(string listenerUrl)
+ {
+ using (var listener = new HttpListener())
+ {
+ listener.Prefixes.Add(listenerUrl);
+
+ Log.InfoFormat("[RETRIEVE] Listening at {0}", listenerUrl);
+
+
+ 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))
+ {
+ 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
{
- if (tc.IsFaulted)
+ 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<NetworkCredential> 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");
}
-
- }
- finally
- {
- listener.Close();
+ return new NetworkCredential(userName, token);
}
- });
-
- 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<string, string> 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("<html><head><title>Authenticated</title></head><body><h1>Got It</h1></body></html>");
+ var html = String.Format("<html><head><title>{0}</title></head><body><h1>{1}</h1></body></html>", title, message);
+ var outBuffer = Encoding.UTF8.GetBytes(html);
response.ContentLength64 = outBuffer.Length;
using (var stream = response.OutputStream)
{
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>