#region /* ----------------------------------------------------------------------- * * * 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. * * ----------------------------------------------------------------------- */ #endregion // // ----------------------------------------------------------------------- using System.IO; using System.Reflection; using Pithos.Client.WPF.Preferences; 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(MethodBase.GetCurrentMethod().DeclaringType); /// /// Asynchronously retrieves PITHOS credentials /// /// URL to retrieve the account info from PITHOS. Must end with = /// The credentials wrapped in a Task public static NetworkCredential RetrieveCredentials(string loginUrl,string accountName=null) { 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 listenerUrl = "pithos://127.0.0.1"; //var receiveCredentials = ListenForRedirectAsync(port); //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; */ } /* 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(); //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)) { 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 { 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 new PithosException("An error occured while retrieving credentials",exc); } } 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; } } }