using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace StarTrinity
{
	static class DigestAuthentication
	{
		public static string GetProxyAuthenticateHeaderLine()
		{
			var rnd = new Random();
			var nonce = GetHash(rnd.Next().ToString());
			var opaque = GetHash(rnd.Next().ToString());

			return String.Format( "Proxy-Authenticate: Digest realm=\"StarTrinity\", qop=\"auth,auth-int\", nonce=\"{0}\", opaque=\"{1}\"", nonce, opaque);
		}
		/// <param name="proxyAuthorizationHeader">
		/// example:Proxy-Authorization: Digest username="tyutyu", realm="StarTrinity", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
		/// 	 uri="/click/dtype=stred/pid=1/cid=70061/path=p0.i-1.w1386.h675/u=1307214116977/reqid=1307214114593885-330285368245401645344458-2-002/*http://yandex.ru/",
		/// 	 response="0673f4c9c13a8f7cfaa36d03b84341c4",
		/// 	 opaque="5ccc069c403ebaf9f0171e9517f40e41", qop=auth, nc=00000008, cnonce="c822c727a648aba7"
		/// </param>
		public static bool CheckDigestResponse(Log log, string proxyAuthorizationHeader, string commandName, Database db, out string user)
		{
            user = null;
			try
            {
                log.Write("digest_auth", null, ">> CheckDigestResponse()");
				Encoding Enc = Encoding.Unicode;

				int pos = proxyAuthorizationHeader.IndexOf(' ');
				String strResponse = proxyAuthorizationHeader.Substring(pos + 1);


				String strUserName, strChalResponse, strRealm,
					strURI, strNonce, strCNonce, strNonceCount, strQop;
				ParseResponse(strResponse, out strUserName, out strChalResponse, out strRealm,
					out strURI, out strNonce, out strCNonce, out strNonceCount,
					out strQop);
                user = strUserName;

				String password = db.GetPasswordForBrowser(strUserName);
                if (String.IsNullOrEmpty(password))
                {
                    log.Write("digest_auth", null, "<< CheckDigestResponse() returns false: correct password is empty");
                    return false;
                }
				
				String A1 = strUserName + ":" + strRealm + ":" + password;
				String A2 = commandName + ":" + strURI;
				String A1Hash = GetHash(A1);
				String A2Hash = GetHash(A2);
				String Response = A1Hash + ":" + strNonce + ":" + strNonceCount + ":" + strCNonce + ":" + strQop + ":" + A2Hash;
				String ResponseHash = GetHash(Response);

                var match = (ResponseHash == strChalResponse);
                log.Write("digest_auth", null, "<< CheckDigestResponse() returns {0}", match);
				return match;
			}
			catch (Exception exc)
			{
                log.WriteException("digest_auth", exc, "authenticating browser");
				return false;
			}
		}
		private static void ParseResponse(String strResponse, out String strUserName, out String StrSplResponse, out String strRealm,
							out String strURI, out String strNonce, out String strCnonce, out String strNonceCount,
							out String strQop
							)
		{
			strUserName = StrSplResponse = strRealm = strURI
			  = strNonce = strCnonce = strNonceCount = strQop = "";

			foreach (string param in strResponse.Split(','))
			{
				int pos = param.IndexOf('=');
				if (pos == -1) continue;
				var name = param.Substring(0, pos).Replace(" ", "");
				var value = param.Substring(pos + 1).Replace(" ", "").Replace("\"", "");

				switch (name)
				{
					case "username":
						strUserName = value;
						break;
					case "nonce":
						strNonce = value;
						break;
					case "realm":
						strRealm = value;
						break;
					case "response":
						StrSplResponse = value;
						break;
					case "uri":
						strURI = value;
						break;
					case "cnonce":
						strCnonce = value;
						break;
					case "nc":
						strNonceCount = value;
						break;
					case "qop":
						strQop = value;
						break;
				}
			}
		}  





		private static String GetHash(String strIn)
		{
			Encoding Enc = Encoding.ASCII;
			MD5 md5 = new MD5CryptoServiceProvider();

			Byte[] bytInPtr = Enc.GetBytes(strIn);
			Byte[] bytMD5Ptr = md5.ComputeHash(bytInPtr);
			String strHash = BitConverter.ToString(bytMD5Ptr);
			strHash = strHash.Replace("-", "").ToLower();

			return strHash;

		}

	}
}
