/*
 * Created by SharpDevelop.
 * User: dmacinto
 * Date: 10/05/11
 * Time: 3:04 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Net;
using System.IO;
using System.Text;

//using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using Org.BouncyCastle.Asn1.Ocsp;

namespace bdf
{
    /// <summary>
    /// Provides wrapper around HttpWebRequest, handling 302 redir, binary and certificate issues
    /// </summary>
	public class Web
	{
		public Web()
		{
		}

		//Login1 (used to acquire token for auth)
        public static bool MakeRequest(string method, string uri, bool json, ref string token, ref byte[] webdata, ref CookieContainer cookie)
        {
            bool redir = true;
            string vars = "";
            //string token = bdf.token;
            WebProxy proxy = null;

            return MakeByteRequest(method, redir, uri, vars, ref webdata, json, ref token, ref cookie, proxy);
        }

		// as above but with RO token option
        public static bool MakeRequest(string method, string uri, bool json, string token, ref byte[] webdata, ref CookieContainer cookie)
        {
            bool redir = true;
            string vars = "";
            WebProxy proxy = null;

            return MakeByteRequest(method, redir, uri, vars, ref webdata, json, ref token, ref cookie, proxy);
        }

        //Login2
        public static bool MakeRequest(string method, string uri, string vars, bool json, ref byte[] webdata, ref CookieContainer cookie)
        {
            bool redir = true;
            string token = bdf.token;
            WebProxy proxy = null;

            return MakeByteRequest(method, redir, uri, vars, ref webdata, json, ref token, ref cookie, proxy);
        }

        //as above with RO token
        public static bool MakeRequest(string method, string uri, string vars, bool json, string token, ref byte[] webdata, ref CookieContainer cookie)
        {
            bool redir = true;
            WebProxy proxy = null;

            return MakeByteRequest(method, redir, uri, vars, ref webdata, json, ref token, ref cookie, proxy);
        }

        /* public static bool MakeRequest(string method, string uri, ref byte[] webdata, ref CookieContainer cookie )
        {
			//byte[] webdata = null;
			bool redir = true;
			string vars = "";
            string token = bdf.token;
            bool json = (token != "");
			WebProxy proxy = null;

            bool success = MakeByteRequest(method, redir, uri, vars, ref webdata, json, token, ref cookie, proxy);
            //string respstr = "";
            //if (webdata != null) { respstr = Encoding.ASCII.GetString(webdata); }
			return success;
        }
		*/

        // old school object call
        public static object[] MakeRequest(string method, bool redir, string uri, string vars, ref CookieContainer cookie, WebProxy proxy)
		{
			byte[] webdata = null;
			string token = bdf.token;
			bool json = true;

            bool success = MakeByteRequest( method, redir,  uri, vars, ref webdata, json, ref token, ref cookie, proxy);
			string respstr = "";
			if ( webdata != null ) { respstr = Encoding.ASCII.GetString(webdata); }
			//Console.WriteLine("{0}",respstr);
			return new object[]{ success, respstr };
		}

		/* public static object[] MakeRequest(string method, bool redir, string uri, string vars, ref CookieContainer cookie, WebProxy proxy)
        {
            object[] r = MakeByteRequest(method, redir, uri, vars, ref cookie, proxy);
            string respstr = "";
            if ((byte[])r[1] != null) { respstr = Encoding.ASCII.GetString((byte[])r[1]); }
            return new object[] { r[0], respstr };
        }
		*/

		// full feature set call
        public static bool MakeByteRequest(string method, bool redir, string uri, string vars, ref byte[] data, bool json, ref string token, ref CookieContainer cookie, WebProxy proxy)
        {
            bool success = false;
            HttpWebRequest webRequest = null;

			// Consider setting webRequest.KeepAlive = false; for the logout call to shut down the TCP resources.			

            if (method == "GET")
            {
                webRequest = (HttpWebRequest)WebRequest.Create(string.Format("{0}{1}", uri, vars));
                webRequest.Method = "GET";
                webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 OPR/76.0.4017.123";

				{   // Try to outsmart Win11/Net481...keep NTLM auth alive
                    webRequest.Credentials = bdf.cache; 
                    webRequest.PreAuthenticate = true; //added for Win11 .NET which fails as it does not auto-redirect
                    webRequest.KeepAlive = true;
                }

                if (token != "")
				{
					webRequest.PreAuthenticate = true;
                    //webRequest.Headers.Add("Authorization", "Bearer " + token);
                    //webRequest.Headers.Add("Origin", uri); //Feb2023
                    //webRequest.Headers.Add("Authorization", "Negotiate " + token);
                    //webRequest.Headers.Add("Authorization", "Negotiate " + token);
                    //webRequest.Headers.Add("Upgrade-Insecure-Requests", "1");
                }
				if (json)
				{ 
                    webRequest.Accept = "application/json";
                }

                webRequest.AllowAutoRedirect = redir;
                webRequest.Method = method;
                webRequest.CookieContainer = cookie;

                webRequest.Timeout = bdf.webMaxWait * 60 * 1000; // convert minutes of wait into milliseconds

                if (proxy != null)
                {
                    webRequest.Proxy = proxy;
                }
            }
            else if (method == "POST")
            {
                byte[] buffer = Encoding.ASCII.GetBytes(vars);
                webRequest = (HttpWebRequest)WebRequest.Create(uri);

				{
                    webRequest.Credentials = bdf.cache;
                    webRequest.PreAuthenticate = true;  //added for Win11 .NET which fails as it does not auto-redirect
					//webRequest.UnsafeAuthenticatedConnectionSharing = true;
					webRequest.KeepAlive = true;
				}

                if (proxy != null)
                {
                    webRequest.Proxy = proxy;
                }
                webRequest.Method = "POST";
                webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 OPR/76.0.4017.123";
                
                if (token != "") //  basically is this a JSON request
				{
					webRequest.PreAuthenticate = true;
					webRequest.Headers.Add("Authorization", "Bearer " + token);
				}
				if (json)
				{
                    webRequest.Accept = "application/json";
                    webRequest.ContentType = "application/json";
                }
                else
                {
                    webRequest.ContentType = "application/x-www-form-urlencoded";
                }

                webRequest.ContentLength = buffer.Length;
                webRequest.AllowAutoRedirect = redir;
                webRequest.Method = method;
                webRequest.CookieContainer = cookie;
                webRequest.Timeout = bdf.webMaxWait * 60 * 1000; // convert minutes of wait into milliseconds

                if (proxy != null)
                {
                    webRequest.Proxy = proxy;
                }

                //We open a stream for writing the postvars
                Stream PostData = webRequest.GetRequestStream();
                //Now we write, and afterwards, we close. Closing is always important!
                PostData.Write(buffer, 0, buffer.Length);
                PostData.Close();
            }
            else
            {
                Logger.Log("Request not understood");
                Logger.Log(3,"FAIL - UNKNOWN METHOD" );
                return false;
            }


            // allows for validation of SSL conversations
            ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(
                ValidateRemoteCertificate
                );

            HttpWebResponse response = null;
            try
            {
				//ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | (SecurityProtocolType)768 | (SecurityProtocolType)192; //3072
				//ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls13;
				try
				{
					response = (HttpWebResponse)webRequest.GetResponse();
				}
				catch (WebException ex)
				{
					//string authresp = Web_Auth.HttpAuthHandler.ExtractAuthenticateHeader(ex);
					//token = authresp.Split(new char[] { ' ' })[1];
					Logger.Log(0, "webRequest failed. {0}", ex.Message);
				}
                //Let's show some information about the response
                //Logger.Log(6, "YY {0}", response.StatusCode);
                //Logger.Log(6, "ZZ {0}", response.GetResponseHeader("error"));

                foreach (Cookie cook in response.Cookies)
                {
                    cookie.Add(cook);
                }

                //DumpCookies(response.Cookies);

                if ((response.StatusCode == HttpStatusCode.Found) ||
                    (response.StatusCode == HttpStatusCode.OK))
                {
                    success = true;
                }
				
                using (Stream s = response.GetResponseStream())
                {
                    //byte[] data = ReadStreamFully(s);
                    data = ReadStreamFully(s);
                    s.Close();
                    //System.IO.StreamWriter file = new System.IO.StreamWriter("webcs.txt"); file.BaseStream.Write(data,0,data.Length);	file.Close();
                    //object[] ret = { success, Encoding.ASCII.GetString(data) , data };
                    //object[] ret = { success, data };
                    //return true;
                }
				
                return success;
				
            }
            catch (Exception ex)
            {
                Logger.Log(0, "WebRequest error: {0}", ex.Message);
                //object[] ret = { false, null };
                return false;
            }
            finally
            {
                if (response != null)
                    response.Close();
            }
        }

        // this version worked for Mono and with a proxy like Fiddler, but not instraight up .NET481 on Win11
        public static bool working_MakeByteRequest(string method, bool redir, string uri, string vars, ref byte[] data, bool json, ref string token, ref CookieContainer cookie, WebProxy proxy)
        {
            bool success = false;
            HttpWebRequest webRequest = null;

            // Consider setting webRequest.KeepAlive = false; for the logout call to shut down the TCP resources.			

            if (method == "GET")
            {
                webRequest = (HttpWebRequest)WebRequest.Create(string.Format("{0}{1}", uri, vars));
                webRequest.Method = "GET";
                webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 OPR/76.0.4017.123";

                {   // Try to outsmart Win11/Net481...
                    webRequest.Credentials = CredentialCache.DefaultNetworkCredentials; //cache;
                    webRequest.UnsafeAuthenticatedConnectionSharing = true;
                    webRequest.UseDefaultCredentials = true;
                    webRequest.PreAuthenticate = true; //added for Win11 .NET which fails as it does not auto-redirect
                    webRequest.KeepAlive = true;
                }

                if (token != "")
                {
                    webRequest.PreAuthenticate = true;
                    //webRequest.Headers.Add("Authorization", "Bearer " + token);
                    //webRequest.Headers.Add("Origin", uri); //Feb2023
                    //webRequest.Headers.Add("Authorization", "Negotiate " + token);
                    //webRequest.Headers.Add("Authorization", "Negotiate " + token);
                    //webRequest.Headers.Add("Upgrade-Insecure-Requests", "1");
                }
                if (json)
                {
                    webRequest.Accept = "application/json";
                }

                webRequest.AllowAutoRedirect = redir;
                webRequest.Method = method;
                webRequest.CookieContainer = cookie;

                webRequest.Timeout = bdf.webMaxWait * 60 * 1000; // convert minutes of wait into milliseconds

                if (proxy != null)
                {
                    webRequest.Proxy = proxy;
                }
            }
            else if (method == "POST")
            {
                byte[] buffer = Encoding.ASCII.GetBytes(vars);
                webRequest = (HttpWebRequest)WebRequest.Create(uri);

                {
                    webRequest.PreAuthenticate = true;  //added for Win11 .NET which fails as it does not auto-redirect
                    webRequest.UnsafeAuthenticatedConnectionSharing = true;
                    webRequest.KeepAlive = true;
                }

                if (proxy != null)
                {
                    webRequest.Proxy = proxy;
                }
                webRequest.Method = "POST";
                webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 OPR/76.0.4017.123";

                if (token != "") //  basically is this a JSON request
                {
                    webRequest.PreAuthenticate = true;
                    webRequest.Headers.Add("Authorization", "Bearer " + token);
                }
                if (json)
                {
                    webRequest.Accept = "application/json";
                    webRequest.ContentType = "application/json";
                }
                else
                {
                    webRequest.ContentType = "application/x-www-form-urlencoded";
                }

                webRequest.ContentLength = buffer.Length;
                webRequest.AllowAutoRedirect = redir;
                webRequest.Method = method;
                webRequest.CookieContainer = cookie;
                webRequest.Timeout = bdf.webMaxWait * 60 * 1000; // convert minutes of wait into milliseconds

                if (proxy != null)
                {
                    webRequest.Proxy = proxy;
                }

                //We open a stream for writing the postvars
                Stream PostData = webRequest.GetRequestStream();
                //Now we write, and afterwards, we close. Closing is always important!
                PostData.Write(buffer, 0, buffer.Length);
                PostData.Close();
            }
            else
            {
                Logger.Log("Request not understood");
                Logger.Log(3, "FAIL - UNKNOWN METHOD");
                return false;
            }


            // allows for validation of SSL conversations
            ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(
                ValidateRemoteCertificate
                );

            HttpWebResponse response = null;
            try
            {
                //ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | (SecurityProtocolType)768 | (SecurityProtocolType)192; //3072
                //ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
                try
                {
                    response = (HttpWebResponse)webRequest.GetResponse();
                }
                catch (WebException ex)
                {
                    //string authresp = Web_Auth.HttpAuthHandler.ExtractAuthenticateHeader(ex);
                    //token = authresp.Split(new char[] { ' ' })[1];
                    Logger.Log(0, "webRequest failed. {0}", ex.Message);
                }
                //Let's show some information about the response
                //Logger.Log(6, "YY {0}", response.StatusCode);
                //Logger.Log(6, "ZZ {0}", response.GetResponseHeader("error"));

                foreach (Cookie cook in response.Cookies)
                {
                    cookie.Add(cook);
                }

                //DumpCookies(response.Cookies);

                if ((response.StatusCode == HttpStatusCode.Found) ||
                    (response.StatusCode == HttpStatusCode.OK))
                {
                    success = true;
                }

                using (Stream s = response.GetResponseStream())
                {
                    //byte[] data = ReadStreamFully(s);
                    data = ReadStreamFully(s);
                    s.Close();
                    //System.IO.StreamWriter file = new System.IO.StreamWriter("webcs.txt"); file.BaseStream.Write(data,0,data.Length);	file.Close();
                    //object[] ret = { success, Encoding.ASCII.GetString(data) , data };
                    //object[] ret = { success, data };
                    //return true;
                }

                return success;

            }
            catch (Exception ex)
            {
                Logger.Log(0, "WebRequest error: {0}", ex.Message);
                //object[] ret = { false, null };
                return false;
            }
            finally
            {
                if (response != null)
                    response.Close();
            }
        }

        /*
        public static object[] zMakeByteRequest(string method, bool redir, string uri, string vars, ref CookieContainer cookie, WebProxy proxy)
		{
			bool success = false;
			HttpWebRequest webRequest = null;	

			// Set the 'Timeout' property in Milliseconds.
			//webRequest.Timeout = 1000 * 60 * 3;  // 3min				

			if ( method == "GET" )
			{
				webRequest = (HttpWebRequest)WebRequest.Create(string.Format("{0}{1}", uri, vars));
				webRequest.Method = "GET";
				webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 OPR/76.0.4017.123";	
				
				if ( bdf.token != "" )
	            {
		            webRequest.PreAuthenticate = true;
					webRequest.Headers.Add("Authorization", "Bearer " + bdf.token );
					webRequest.Accept = "application/json";
	            }
				
				webRequest.AllowAutoRedirect = redir;
				webRequest.Method = method;
				webRequest.CookieContainer = cookie;
				
				//webRequest.Timeout = 180000; // 3min
				webRequest.Timeout = bdf.webMaxWait * 60 * 1000; // convert minutes of wait into milliseconds
				
				if (proxy != null)
				{
					webRequest.Proxy = proxy;
				}
			}
			else if ( method == "POST" )
			{
				byte[]  buffer = Encoding.ASCII.GetBytes(vars);
	            webRequest = (HttpWebRequest)WebRequest.Create(uri);
				if (proxy != null)
				{
					webRequest.Proxy = proxy;
				}	            
	            webRequest.Method = "POST";
	            webRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 OPR/76.0.4017.123";
	            
	            
				if ( bdf.token != "" ) //  basically is this a JSON request
	            {
					webRequest.PreAuthenticate = true;
					webRequest.Headers.Add("Authorization", "Bearer " + bdf.token );
					webRequest.Accept = "application/json";
                    webRequest.ContentType = "application/json";
                }
				else
				{
                    webRequest.ContentType = "application/x-www-form-urlencoded";
                    webRequest.ContentType = "application/json";		// all bdf requests are json
                }
	            
	            webRequest.ContentLength = buffer.Length;
	            webRequest.AllowAutoRedirect = redir;
				webRequest.Method = method;
				webRequest.CookieContainer = cookie;
                webRequest.Timeout = bdf.webMaxWait * 60 * 1000; // convert minutes of wait into milliseconds

                if (proxy != null)
				{
					webRequest.Proxy = proxy;
				}
				
	            //We open a stream for writing the postvars
	            Stream PostData = webRequest.GetRequestStream();
	            //Now we write, and afterwards, we close. Closing is always important!
	            PostData.Write(buffer, 0, buffer.Length);
	            PostData.Close();
			}
			else 
			{
				Logger.Log("Request not understood");
				object[] ret = { false, "FAIL - UNKNOWN METHOD" };
				return ret;
			}
			

			// allows for validation of SSL conversations
			ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(
				ValidateRemoteCertificate
				);

			HttpWebResponse response = null;
			
			try
			{
				//ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 | (SecurityProtocolType)768 | (SecurityProtocolType)192; //3072
				//ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
				response = (HttpWebResponse)webRequest.GetResponse();
				
				//Let's show some information about the response
		        Logger.Log(6,"{0}",response.StatusCode);	            

		        foreach (Cookie cook in response.Cookies)
				{
			    	cookie.Add(cook);
				}
		        
		        //DumpCookies(response.Cookies);
	            
	            if ( ( response.StatusCode == HttpStatusCode.Found ) ||
	                ( response.StatusCode == HttpStatusCode.OK ) )
	            {
	            	success = true;	        
	            }
				
				using (Stream s = response.GetResponseStream() )
				{
					byte[] data = ReadStreamFully(s);
					s.Close();	
					//System.IO.StreamWriter file = new System.IO.StreamWriter("webcs.txt"); file.BaseStream.Write(data,0,data.Length);	file.Close();
					//object[] ret = { success, Encoding.ASCII.GetString(data) , data };
					object[] ret = { success, data };
					return ret;
				}
			}
			catch (Exception ex)
			{
				Logger.Log(0,"WebRequest error: {0}",ex.Message);
				object[] ret = { false, null };
				return ret;
			}
			finally
			{
				if (response != null)
					response.Close();
			}
		}
		*/

        public static byte[] ReadStreamFully (Stream stream)
		{
		    byte[] buffer = new byte[32768];
		    using (MemoryStream ms = new MemoryStream())
		    {
		        while (true)
		        {
		            int read = stream.Read (buffer, 0, buffer.Length);
		            if (read <= 0)
		                //return ms;
		            	return ms.ToArray();
		            ms.Write (buffer, 0, read);
		        }
		    }
		}
		
		// callback used to validate the certificate in an SSL conversation
		private static bool ValidateRemoteCertificate(
		object sender,
			X509Certificate certificate,
			X509Chain chain,
			SslPolicyErrors policyErrors
		)
		{
			/*if (Convert.ToBoolean(ConfigurationManager.AppSettings["IgnoreSslErrors"]))
			{
				// allow any old dodgy certificate...
				return true;
			}
			else
			{
				return policyErrors == SslPolicyErrors.None;
			}
			*/
			
			return true; //policyErrors == SslPolicyErrors.None;
		}
		/*
		private static void DumpCookies ( System.Net.CookieCollection ckjar )
		{
			foreach (Cookie cook in ckjar)
            //foreach (Cookie cook in cookie)
            {
                Logger.Log("Cookie:");
                Logger.Log(0, "{0} = {1}", cook.Name, cook.Value);
                Logger.Log(0, "Domain: {0}", cook.Domain);
                Logger.Log(0, "Path: {0}", cook.Path);
                Logger.Log(0, "Port: {0}", cook.Port);
                Logger.Log(0, "Secure: {0}", cook.Secure);
             
                Logger.Log(0, "When issued: {0}", cook.TimeStamp);
                Logger.Log(0, "Expires: {0} (expired? {1})", 
                    cook.Expires, cook.Expired);
                Logger.Log(0, "Don't save: {0}", cook.Discard);    
                Logger.Log(0, "Comment: {0}", cook.Comment);
                Logger.Log(0, "Uri for comments: {0}", cook.CommentUri);
                Logger.Log(0, "Version: RFC {0}" , cook.Version == 1 ? "2109" : "2965");

                // Show the string representation of the cookie.
                Logger.Log (0, "String: {0}", cook.ToString());
            }
		}
		*/
		
	}
}
