//
// Mono.Google.Authentication.cs:
//
// Authors:
//	Gonzalo Paniagua Javier (gonzalo@ximian.com)
//	Stephane Delcroix (stephane@delcroix.org)
//
// (C) Copyright 2006 Novell, Inc. (http://www.novell.com)
// (C) Copyright 2007 S. Delcroix
//

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Check the Google Authentication Page at http://code.google.com/apis/accounts/AuthForInstalledApps.html
//

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Web;

namespace Mono.Google {
	class Authentication {
		static string client_login_url = "https://www.google.com/accounts/ClientLogin";

		public static string GetAuthorization (GoogleConnection conn, string email, string password,
				GoogleService service, string token, string captcha)
		{
			if (email == null || email == String.Empty || password == null || password == String.Empty)
				return null;

			email = HttpUtility.UrlEncode (email);
			password = HttpUtility.UrlEncode (password);
			string appname = HttpUtility.UrlEncode (conn.ApplicationName);
			string service_code = service.ServiceCode;

			StringBuilder content = new StringBuilder ();
			content.Append ("accountType=GOOGLE");
			content.AppendFormat ("&Email={0}", email);
			content.AppendFormat ("&Passwd={0}", password);
			content.AppendFormat ("&service={0}", service_code);
			content.AppendFormat ("&source={0}", appname);

			if (token != null) {
				content.AppendFormat ("&logintoken={0}", token);
				content.AppendFormat ("&logincaptcha={0}", captcha);
			}
			byte [] bytes = Encoding.UTF8.GetBytes (content.ToString ());

			HttpWebRequest request = (HttpWebRequest) WebRequest.Create (client_login_url);
			request.Method = "POST";
			request.ContentType = "application/x-www-form-urlencoded";
			request.ContentLength = bytes.Length;

			Stream output = request.GetRequestStream ();
			output.Write (bytes, 0, bytes.Length);
			output.Close ();

			HttpWebResponse response = null;
			try {
				response = (HttpWebResponse) request.GetResponse ();
			} catch (WebException wexc) {
				response = wexc.Response as HttpWebResponse;
				if (response == null)
					throw;
				ThrowOnError (response);
				throw; // if the method above does not throw, we do
			}

			//string sid = null;
			//string lsid = null;
			string auth = null;

			using (Stream stream = response.GetResponseStream ()) {
				StreamReader sr = new StreamReader (stream, Encoding.UTF8);
				string s;
				while ((s = sr.ReadLine ()) != null) {
					if (s.StartsWith ("Auth="))
						auth = s.Substring (5);
					//else if (s.StartsWith ("LSID="))
					//	lsid = s.Substring (5);
					//else if (s.StartsWith ("SID="))
					//	sid = s.Substring (4);
				}
			}
			response.Close ();

			return auth;
		}

		static void ThrowOnError (HttpWebResponse response)
		{
			if (response.StatusCode != HttpStatusCode.Forbidden)
				return;

			string url = null;
			string token = null;
			string captcha_url = null;
			string code = null;
			using (StreamReader reader = new StreamReader (response.GetResponseStream ())) {
				string str;
				while ((str = reader.ReadLine ()) != null) {
					if (str.StartsWith ("Url=")) {
						url = str.Substring (4);
					} else if (str.StartsWith ("Error=")) {
						/* These are the values for Error
							None,
							BadAuthentication,
							NotVerified,
							TermsNotAgreed,
							CaptchaRequired,
							Unknown,
							AccountDeleted,
							AccountDisabled,
							ServiceUnavailable
						*/
						code = str.Substring (6);
					} else if (str.StartsWith ("CaptchaToken=")) {
						token = str.Substring (13);
					} else if (str.StartsWith ("CaptchaUrl=")) {
						captcha_url = str.Substring (11);
					}
				}
			}
			if (code == "CaptchaRequired" && token != null && captcha_url != null) {
				if (url != null) {
					Uri uri = new Uri (url);
					captcha_url = new Uri (uri, captcha_url).ToString ();
				} else if (!captcha_url.StartsWith ("https://")) {
					captcha_url = "https://www.google.com/accounts/" + captcha_url;
				}
				throw new CaptchaException (url, token, captcha_url);
			}

			throw new UnauthorizedAccessException (String.Format ("Access to '{0}' is denied ({1})", url, code));
		}
	}
}
