#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;
}
}
}