// -----------------------------------------------------------------------
-// <copyright file="PithosAccount.cs" company="Microsoft">
-// TODO: Update copyright text.
+// <copyright file="PithosAccount.cs" company="GRNet">
+// Copyright 2011 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>
// -----------------------------------------------------------------------
+using System.IO;
using Pithos.Network;
using log4net;
/// <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 Task<NetworkCredential> 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();
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<HttpListenerContext>(listener.BeginGetContext, listener.EndGetContext, null)
- .WithTimeout(TimeSpan.FromMinutes(5));
+ 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);
- 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(pithosSite);
- uriBuilder.Path="login";
- 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;
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>