// </copyright>
// -----------------------------------------------------------------------
+using System.IO;
using Pithos.Network;
using log4net;
/// </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));
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;
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>