Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / PithosAccount.cs @ 03ee454f

History | View | Annotate | Download (8 kB)

1
// -----------------------------------------------------------------------
2
// <copyright file="PithosAccount.cs" company="Microsoft">
3
// TODO: Update copyright text.
4
// </copyright>
5
// -----------------------------------------------------------------------
6

    
7
using System.IO;
8
using Pithos.Network;
9
using log4net;
10

    
11
namespace Pithos.Client.WPF
12
{
13
    using System;
14
    using System.Collections.Generic;
15
    using System.Linq;
16
    using System.Text;
17
    using System.Net;
18
    using System.Threading.Tasks;
19
    using System.Diagnostics.Contracts;
20
    using System.Diagnostics;
21
    using System.Net.Sockets;
22

    
23
    /// <summary>
24
    /// TODO: Update summary.
25
    /// </summary>
26
    /// <summary>
27
    /// Retrieves an account name and token from PITHOS
28
    /// </summary>
29
    public static class PithosAccount
30
    {
31
        private static readonly ILog Log = LogManager.GetLogger(typeof(PithosAccount));
32

    
33
        /// <summary>
34
        /// Asynchronously retrieves PITHOS credentials
35
        /// </summary>
36
        /// <param name="loginUrl">URL to retrieve the account info from PITHOS. Must end with =</param>
37
        /// <returns>The credentials wrapped in a Task</returns>
38
        public static Task<NetworkCredential> RetrieveCredentials(string loginUrl)
39
        {
40
            Contract.Requires(Uri.IsWellFormedUriString(loginUrl, UriKind.Absolute));
41

    
42
            if (!Uri.IsWellFormedUriString(loginUrl, UriKind.Absolute))
43
                throw new ArgumentException("The pithosSite parameter must be a valid absolute URL", "loginUrl");
44
            
45
            int port = GetFreePort();
46

    
47

    
48
            var listenerUrl = String.Format("http://127.0.0.1:{0}/", port);
49

    
50
            
51

    
52
            var receiveCredentials = ListenForRedirectAsync(port);
53

    
54
            var uriBuilder=new UriBuilder(loginUrl);                       
55
            uriBuilder.Query="next=" + listenerUrl;
56

    
57
            var retrieveUri = uriBuilder.Uri;
58
            Log.InfoFormat("[RETRIEVE] Open Browser at {0}", retrieveUri);
59
            Process.Start(retrieveUri.ToString());
60

    
61
            return receiveCredentials;
62
        }
63

    
64
        private static async Task<NetworkCredential> ListenHttpForRedirectAsync(string listenerUrl)
65
        {
66
            using (var listener = new HttpListener())
67
            {
68
                listener.Prefixes.Add(listenerUrl);
69

    
70
                Log.InfoFormat("[RETRIEVE] Listening at {0}", listenerUrl);
71

    
72

    
73
                try
74
                {
75
                    listener.Start();
76

    
77
                    var context = await listener.GetContextAsync()
78
                                            .WithTimeout(TimeSpan.FromMinutes(5));
79

    
80
                    var request = context.Request;
81
                    Log.InfoFormat("[RETRIEVE] Got Connection {0}", request.RawUrl);
82

    
83
                    var query = request.QueryString;
84
                    var userName = query["user"];
85
                    var token = query["token"];
86
                    if (String.IsNullOrWhiteSpace(userName) ||
87
                        String.IsNullOrWhiteSpace(token))
88
                    {
89
                        Respond(context, "Failure", "The server did not return a username or token");
90
                        Log.ErrorFormat("[RETRIEVE] No credentials returned by server");
91
                        throw new Exception("The server did not return a username or token");
92
                    }
93
                    else
94
                    {
95
                        Log.InfoFormat("[RETRIEVE] Credentials retrieved user:{0} token:{1}", userName, token);
96
                        Respond(context, "Authenticated", "Got It");
97

    
98
                        return new NetworkCredential(userName, token);
99
                    }
100
                }
101
                catch (Exception exc)
102
                {
103
                    Log.Error("[RETRIEVE][ERROR] Receive connection {0}", exc);
104
                    throw;
105
                }
106
            }
107
        }
108

    
109
        private static async Task<NetworkCredential> ListenForRedirectAsync(int port)
110
        {
111
            var listener = new TcpListener(IPAddress.Any, port);            
112
            Log.InfoFormat("[RETRIEVE] Listening at {0}", port);
113

    
114
            try
115
            {
116
                listener.Start();
117

    
118
                using (var client = await listener.AcceptTcpClientAsync()
119
                                        .WithTimeout(TimeSpan.FromMinutes(5)))
120
                {
121

    
122
                    using (var stream = client.GetStream())
123
                    using (var reader=new StreamReader(stream))
124
                    {
125
                        var request = await reader.ReadLineAsync();
126
                        Log.InfoFormat("[RETRIEVE] Got Connection {0}", request);
127

    
128

    
129
                        var parts=request.Split(' ');
130
                        var query = parts[1].TrimStart('/','?');
131

    
132
                        var items = query.Split('&')
133
                            .Select(pair => pair.Split('='))
134
                            .ToDictionary(arr => arr[0].ToLower(), arr => Uri.UnescapeDataString(arr[1]));
135

    
136
                        var userName = items["user"];
137
                        var token = items["token"];
138
                        if (String.IsNullOrWhiteSpace(userName) ||
139
                            String.IsNullOrWhiteSpace(token))
140
                        {
141
                            Respond(stream, "Failure", "The server did not return a username or token");
142
                            Log.ErrorFormat("[RETRIEVE] No credentials returned by server");
143
                            throw new Exception("The server did not return a username or token");
144
                        }
145
                        else
146
                        {
147
                            Log.InfoFormat("[RETRIEVE] Credentials retrieved user:{0} token:{1}", userName, token);
148
                            Respond(stream, "Authenticated", "Got It");                            
149
                        }
150
                        return new NetworkCredential(userName, token);
151
                    }
152
                }
153
            }
154
            catch (Exception exc)
155
            {
156
                Log.Error("[RETRIEVE][ERROR] Receive connection {0}", exc);
157
                throw;
158
            }
159
        }
160

    
161

    
162

    
163
        private static void Respond(HttpListenerContext context,string title,string message)
164
        {
165
            var response = context.Response;
166
            var html = String.Format("<html><head><title>{0}</title></head><body><h1>{1}</h1></body></html>", title, message);
167
            var outBuffer = Encoding.UTF8.GetBytes(html);
168
            response.ContentLength64 = outBuffer.Length;
169
            using (var stream = response.OutputStream)
170
            {
171
                stream.Write(outBuffer, 0, outBuffer.Length);
172
            }
173

    
174
            Log.InfoFormat("[RETRIEVE] Responded");
175
        }
176

    
177
        private static void Respond(Stream stream,string title,string message)
178
        {
179
            
180
            var html = String.Format("<html><head><title>{0}</title></head><body><h1>{1}</h1></body></html>", title, message);
181
            
182
            var response = new StringBuilder();
183
            response.AppendLine("HTTP/1.1 200 OK");
184
            response.AppendFormat("Content-Length: {0}\n", html.Length);
185
            response.AppendLine("Server: Microsoft-HTTPAPI/2.0");
186
            response.AppendFormat("Date: {0}\n\n", DateTime.UtcNow);
187
            response.AppendLine(html);
188

    
189
            var outBuffer=Encoding.UTF8.GetBytes(response.ToString());
190
            
191
            stream.Write(outBuffer, 0, outBuffer.Length);
192

    
193
            Log.InfoFormat("[RETRIEVE] Responded");
194
        }
195

    
196
       
197
        /// <summary>
198
        /// Locates a free local port 
199
        /// </summary>
200
        /// <returns>A free port</returns>
201
        /// <remarks>The method starts and stops a TcpListener on port 0 to locate a free port.</remarks>
202
        public static int GetFreePort()
203
        {
204
            //The TcpListener will locate a free port             
205
            var listener = new TcpListener(IPAddress.Any, 0);
206
            listener.Start();
207
            var port = ((IPEndPoint)listener.LocalEndpoint).Port;
208
            listener.Stop();
209
            return port;
210
        }
211
    }
212
}