// ----------------------------------------------------------------------- // // 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. // // ----------------------------------------------------------------------- using System.IO; using Pithos.Network; using log4net; namespace Pithos.Client.WPF { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Threading.Tasks; using System.Diagnostics.Contracts; using System.Diagnostics; using System.Net.Sockets; /// /// TODO: Update summary. /// /// /// Retrieves an account name and token from PITHOS /// public static class PithosAccount { private static readonly ILog Log = LogManager.GetLogger(typeof(PithosAccount)); /// /// Asynchronously retrieves PITHOS credentials /// /// URL to retrieve the account info from PITHOS. Must end with = /// The credentials wrapped in a Task public static Task RetrieveCredentials(string loginUrl) { Contract.Requires(Uri.IsWellFormedUriString(loginUrl, UriKind.Absolute)); 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); 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 ListenHttpForRedirectAsync(string listenerUrl) { using (var listener = new HttpListener()) { listener.Prefixes.Add(listenerUrl); Log.InfoFormat("[RETRIEVE] Listening at {0}", listenerUrl); 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 { 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(); if (request == null) throw new Exception("Nothing retrieved"); 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)) { 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 Dictionary ParseResponse(string 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])); return items; } private static void Respond(HttpListenerContext context,string title,string message) { var response = context.Response; var html = String.Format("{0}

{1}

", title, message); var outBuffer = Encoding.UTF8.GetBytes(html); response.ContentLength64 = outBuffer.Length; using (var stream = response.OutputStream) { stream.Write(outBuffer, 0, outBuffer.Length); } 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 /// /// A free port /// The method starts and stops a TcpListener on port 0 to locate a free port. public static int GetFreePort() { //The TcpListener will locate a free port var listener = new TcpListener(IPAddress.Any, 0); listener.Start(); var port = ((IPEndPoint)listener.LocalEndpoint).Port; listener.Stop(); return port; } } }