//
// System.Web.HttoWorkerRequest.cs 
//
// Author:
//	Miguel de Icaza (miguel@novell.com)
//

//
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
//
// 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.
//

using System.Collections;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Web.UI;
using System.Collections.Specialized;

namespace System.Web {

	// CAS
	[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	[AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	// attributes
	[ComVisible (false)]
	public abstract class HttpWorkerRequest {

		public delegate void EndOfSendNotification (HttpWorkerRequest wr, object extraData);

		public const int HeaderCacheControl = 0;
		public const int HeaderConnection = 1;
		public const int HeaderDate = 2;
		public const int HeaderKeepAlive = 3;
		public const int HeaderPragma = 4;
		public const int HeaderTrailer = 5;
		public const int HeaderTransferEncoding = 6;
		public const int HeaderUpgrade = 7;
		public const int HeaderVia = 8;
		public const int HeaderWarning = 9;
		public const int HeaderAllow = 10;
		public const int HeaderContentLength = 11;
		public const int HeaderContentType = 12;
		public const int HeaderContentEncoding = 13;
		public const int HeaderContentLanguage = 14;
		public const int HeaderContentLocation = 15;
		public const int HeaderContentMd5 = 16;
		public const int HeaderContentRange = 17;
		public const int HeaderExpires = 18;
		public const int HeaderLastModified = 19;
		public const int HeaderAccept = 20;
		public const int HeaderAcceptCharset = 21;
		public const int HeaderAcceptEncoding = 22;
		public const int HeaderAcceptLanguage = 23;
		public const int HeaderAuthorization = 24;
		public const int HeaderCookie = 25;
		public const int HeaderExpect = 26;
		public const int HeaderFrom = 27;
		public const int HeaderHost = 28;
		public const int HeaderIfMatch = 29;
		public const int HeaderIfModifiedSince = 30;
		public const int HeaderIfNoneMatch = 31;
		public const int HeaderIfRange = 32;
		public const int HeaderIfUnmodifiedSince = 33;
		public const int HeaderMaxForwards = 34;
		public const int HeaderProxyAuthorization = 35;
		public const int HeaderReferer = 36;
		public const int HeaderRange = 37;
		public const int HeaderTe = 38;
		public const int HeaderUserAgent = 39;
		public const int RequestHeaderMaximum = 40;

		public const int HeaderAcceptRanges = 20;
		public const int HeaderAge = 21;
		public const int HeaderEtag = 22;
		public const int HeaderLocation = 23;
		public const int HeaderProxyAuthenticate = 24;
		public const int HeaderRetryAfter = 25;
		public const int HeaderServer = 26;
		public const int HeaderSetCookie = 27;
		public const int HeaderVary = 28;
		public const int HeaderWwwAuthenticate = 29;
		public const int ResponseHeaderMaximum = 30;

		public const int ReasonResponseCacheMiss = 0;
		public const int ReasonFileHandleCacheMiss = 1;
		public const int ReasonCachePolicy = 2;
		public const int ReasonCacheSecurity = 3;
		public const int ReasonClientDisconnect = 4;
		public const int ReasonDefault = 0;

		static readonly Hashtable RequestHeaderIndexer;
		static readonly Hashtable ResponseHeaderIndexer;

		static HttpWorkerRequest() {
			RequestHeaderIndexer = CollectionsUtil.CreateCaseInsensitiveHashtable(RequestHeaderMaximum);
			for (int i = 0; i < RequestHeaderMaximum; i++)
				RequestHeaderIndexer[GetKnownRequestHeaderName(i)] = i;

			ResponseHeaderIndexer = CollectionsUtil.CreateCaseInsensitiveHashtable(ResponseHeaderMaximum);
			for (int i = 0; i < ResponseHeaderMaximum; i++)
				ResponseHeaderIndexer[GetKnownResponseHeaderName(i)] = i;
		}

		public virtual string MachineConfigPath {
			get {
				return null;
			}
		}

		public virtual string MachineInstallDirectory {
			get {
				return null;
			}
		}

#if NET_2_0
		public virtual Guid RequestTraceIdentifier {
			get { return Guid.Empty; }
		}

		public virtual string RootWebConfigPath {
			get { return null; }
		}
#endif

		public virtual void CloseConnection ()
		{
		}

		public virtual string GetAppPath ()
		{
			return null;
		}

		public virtual string GetAppPathTranslated ()
		{
			return null;
		}

		public virtual string GetAppPoolID ()
		{
			return null;
		}
		
		public virtual long GetBytesRead ()
		{
			return 0;
		}

		public virtual string GetFilePath ()
		{
			return null;
		}

		public virtual string GetFilePathTranslated ()
		{
			return null;
		}

		public virtual string GetKnownRequestHeader (int index)
		{
			return null;
		}

		public virtual string GetPathInfo ()
		{
			return "";
		}

		public virtual byte [] GetPreloadedEntityBody ()
		{
			return null;
		}
#if NET_2_0
		public virtual int GetPreloadedEntityBody (byte[] buffer, int offset)
		{
			return 0;
		}

		public virtual int GetPreloadedEntityBodyLength ()
		{
			return 0;
		}
#endif
		public virtual string GetProtocol ()
		{
			if (IsSecure ())
				return "https";
			else
				return "http";
		}
		
		public virtual byte [] GetQueryStringRawBytes ()
		{
			return null;
		}

		public virtual string GetRemoteName ()
		{
			return GetRemoteAddress ();
		}
		
		public virtual int GetRequestReason ()
		{
			return 0;
		}

		public virtual string GetServerName ()
		{
			return GetLocalAddress ();
		}

		public virtual string GetServerVariable (string name)
		{
			return null;
		}
#if NET_2_0
		public virtual int GetTotalEntityBodyLength ()
		{
			return 0;
		}
#endif
		public virtual string GetUnknownRequestHeader (string name)
		{
			return null;
		}

		[CLSCompliant (false)]
		public virtual string [][] GetUnknownRequestHeaders ()
		{
			return null;
		}

		public virtual IntPtr GetUserToken ()
		{
			return IntPtr.Zero;
		}

		public bool HasEntityBody ()
		{
			// TODO this is not virtual.
			
			return false;
		}

		public virtual bool HeadersSent ()
		{
			return true;
		}

		public virtual bool IsClientConnected ()
		{
			return true;
		}

		public virtual bool IsEntireEntityBodyIsPreloaded ()
		{
			return false;
		}

		public virtual bool IsSecure ()
		{
			return false;
		}

		public virtual string MapPath (string virtualPath)
		{
			return null;
		}

		public virtual int ReadEntityBody (byte [] buffer, int size)
		{
			return 0;
		}
#if NET_2_0
		public virtual int ReadEntityBody (byte [] buffer, int offset, int size)
		{
			return 0;
		}
#endif
		public virtual void SendCalculatedContentLength (int contentLength)
		{
			throw new NotImplementedException ();
		}

#if TARGET_JVM
		public virtual void SendResponseFromMemory (IntPtr data, int length)
		{
			throw new NotImplementedException("SendResponseFromMemory: unsafe buffers (IntPtr) are not supported");
		}
#else
		public virtual void SendResponseFromMemory (IntPtr data, int length)
		{
			if (data != IntPtr.Zero) {
				byte [] copy = new byte [length];
				Marshal.Copy (data, copy, 0, length);
				SendResponseFromMemory (copy, length);
			}
		}
#endif

		public virtual void SetEndOfSendNotification (HttpWorkerRequest.EndOfSendNotification callback, object extraData)
		{
		}

#region Abstract methods
		public abstract void EndOfRequest ();
		public abstract void FlushResponse (bool finalFlush);
		public abstract string GetHttpVerbName ();
		public abstract string GetHttpVersion ();
		public abstract string GetLocalAddress ();
		public abstract int GetLocalPort ();
		public abstract string GetQueryString ();
		public abstract string GetRawUrl ();
		public abstract string GetRemoteAddress ();
		public abstract int GetRemotePort ();
		public abstract string GetUriPath ();
		public abstract void SendKnownResponseHeader (int index, string value);
		public abstract void SendResponseFromFile (IntPtr handle, long offset, long length);
		public abstract void SendResponseFromFile (string filename, long offset, long length);
		public abstract void SendResponseFromMemory (byte [] data, int length);
		public abstract void SendStatus (int statusCode, string statusDescription);
		public abstract void SendUnknownResponseHeader (string name, string value);
#endregion
		
#region Static methods
		
		public static int GetKnownRequestHeaderIndex (string header)
		{
			switch (header){
			case "Cache-Control": return HeaderCacheControl;
			case "Connection": return HeaderConnection;
			case "Date": return HeaderDate;
			case "Keep-Alive": return HeaderKeepAlive;
			case "Pragma": return HeaderPragma;
			case "Trailer": return HeaderTrailer;
			case "Transfer-Encoding": return HeaderTransferEncoding;
			case "Upgrade": return HeaderUpgrade;
			case "Via": return HeaderVia;
			case "Warning": return HeaderWarning;
			case "Allow": return HeaderAllow;
			case "Content-Length": return HeaderContentLength;
			case "Content-Type": return HeaderContentType;
			case "Content-Encoding": return HeaderContentEncoding;
			case "Content-Language": return HeaderContentLanguage;
			case "Content-Location": return HeaderContentLocation;
			case "Content-MD5": return HeaderContentMd5;
			case "Content-Range": return HeaderContentRange;
			case "Expires": return HeaderExpires;
			case "Last-Modified": return HeaderLastModified;
			case "Accept": return HeaderAccept;
			case "Accept-Charset": return HeaderAcceptCharset;
			case "Accept-Encoding": return HeaderAcceptEncoding;
			case "Accept-Language": return HeaderAcceptLanguage;
			case "Authorization": return HeaderAuthorization;
			case "Cookie": return HeaderCookie;
			case "Expect": return HeaderExpect;
			case "From": return HeaderFrom;
			case "Host": return HeaderHost;
			case "If-Match": return HeaderIfMatch;
			case "If-Modified-Since": return HeaderIfModifiedSince;
			case "If-None-Match": return HeaderIfNoneMatch;
			case "If-Range": return HeaderIfRange;
			case "If-Unmodified-Since": return HeaderIfUnmodifiedSince;
			case "Max-Forwards": return HeaderMaxForwards;
			case "Proxy-Authorization": return HeaderProxyAuthorization;
			case "Referer": return HeaderReferer;
			case "Range": return HeaderRange;
			case "TE": return HeaderTe;
			case "User-Agent": return HeaderUserAgent;
			}

			object index = RequestHeaderIndexer[header];
			if (index != null)
				return (int)index;

			return -1;
		}

		public static string GetKnownRequestHeaderName (int index)
		{
			switch (index){
			case HeaderCacheControl: return "Cache-Control";
			case HeaderConnection: return "Connection";
			case HeaderDate: return "Date";
			case HeaderKeepAlive: return "Keep-Alive";
			case HeaderPragma: return "Pragma";
			case HeaderTrailer: return "Trailer";
			case HeaderTransferEncoding: return "Transfer-Encoding";
			case HeaderUpgrade: return "Upgrade";
			case HeaderVia: return "Via";
			case HeaderWarning: return "Warning";
			case HeaderAllow: return "Allow";
			case HeaderContentLength: return "Content-Length";
			case HeaderContentType: return "Content-Type";
			case HeaderContentEncoding: return "Content-Encoding";
			case HeaderContentLanguage: return "Content-Language";
			case HeaderContentLocation: return "Content-Location";
			case HeaderContentMd5: return "Content-MD5";
			case HeaderContentRange: return "Content-Range";
			case HeaderExpires: return "Expires";
			case HeaderLastModified: return "Last-Modified";
			case HeaderAccept: return "Accept";
			case HeaderAcceptCharset: return "Accept-Charset";
			case HeaderAcceptEncoding: return "Accept-Encoding";
			case HeaderAcceptLanguage: return "Accept-Language";
			case HeaderAuthorization: return "Authorization";
			case HeaderCookie: return "Cookie";
			case HeaderExpect: return "Expect";
			case HeaderFrom: return "From";
			case HeaderHost: return "Host";
			case HeaderIfMatch: return "If-Match";
			case HeaderIfModifiedSince: return "If-Modified-Since";
			case HeaderIfNoneMatch: return "If-None-Match";
			case HeaderIfRange: return "If-Range";
			case HeaderIfUnmodifiedSince: return "If-Unmodified-Since";
			case HeaderMaxForwards: return "Max-Forwards";
			case HeaderProxyAuthorization: return "Proxy-Authorization";
			case HeaderReferer: return "Referer";
			case HeaderRange: return "Range";
			case HeaderTe: return "TE";
			case HeaderUserAgent: return "User-Agent";
			}
			throw new IndexOutOfRangeException ("index");
				
		}

		public static int GetKnownResponseHeaderIndex (string header)
		{
			switch (header){
			case "Cache-Control": return HeaderCacheControl;
			case "Connection": return HeaderConnection;
			case "Date": return HeaderDate;
			case "Keep-Alive": return HeaderKeepAlive;
			case "Pragma": return HeaderPragma;
			case "Trailer": return HeaderTrailer;
			case "Transfer-Encoding": return HeaderTransferEncoding;
			case "Upgrade": return HeaderUpgrade;
			case "Via": return HeaderVia;
			case "Warning": return HeaderWarning;
			case "Allow": return HeaderAllow;
			case "Content-Length": return HeaderContentLength;
			case "Content-Type": return HeaderContentType;
			case "Content-Encoding": return HeaderContentEncoding;
			case "Content-Language": return HeaderContentLanguage;
			case "Content-Location": return HeaderContentLocation;
			case "Content-MD5": return HeaderContentMd5;
			case "Content-Range": return HeaderContentRange;
			case "Expires": return HeaderExpires;
			case "Last-Modified": return HeaderLastModified;
			case "Accept-Ranges": return HeaderAcceptRanges;
			case "Age": return HeaderAge;
			case "ETag": return HeaderEtag;
			case "Location": return HeaderLocation;
			case "Proxy-Authenticate":return HeaderProxyAuthenticate;
			case "Retry-After": return HeaderRetryAfter;
			case "Server": return HeaderServer;
			case "Set-Cookie": return HeaderSetCookie;
			case "Vary": return HeaderVary;
			case "WWW-Authenticate": return HeaderWwwAuthenticate;
			}

			object index = ResponseHeaderIndexer[header];
			if (index != null)
				return (int)index;

			return -1;
		}

		public static string GetKnownResponseHeaderName (int index)
		{
			switch (index){
			case HeaderCacheControl: return "Cache-Control";
			case HeaderConnection: return "Connection";
			case HeaderDate: return "Date";
			case HeaderKeepAlive: return "Keep-Alive";
			case HeaderPragma: return "Pragma";
			case HeaderTrailer: return "Trailer";
			case HeaderTransferEncoding: return "Transfer-Encoding";
			case HeaderUpgrade: return "Upgrade";
			case HeaderVia: return "Via";
			case HeaderWarning: return "Warning";
			case HeaderAllow: return "Allow";
			case HeaderContentLength: return "Content-Length";
			case HeaderContentType: return "Content-Type";
			case HeaderContentEncoding: return "Content-Encoding";
			case HeaderContentLanguage: return "Content-Language";
			case HeaderContentLocation: return "Content-Location";
			case HeaderContentMd5: return "Content-MD5";
			case HeaderContentRange: return "Content-Range";
			case HeaderExpires: return "Expires";
			case HeaderLastModified: return "Last-Modified";
			case HeaderAcceptRanges: return "Accept-Ranges";
			case HeaderAge: return "Age";
			case HeaderEtag: return "ETag";
			case HeaderLocation: return "Location";
			case HeaderProxyAuthenticate: return "Proxy-Authenticate";
			case HeaderRetryAfter: return "Retry-After";
			case HeaderServer: return "Server";
			case HeaderSetCookie: return "Set-Cookie";
			case HeaderVary: return "Vary";
			case HeaderWwwAuthenticate: return "WWW-Authenticate";
			}

			throw new IndexOutOfRangeException ("index");
		}
		
		public static string GetStatusDescription (int code)
		{
			switch (code){
			case 100: return "Continue";
			case 101: return "Switching Protocols";
			case 102: return "Processing";
			case 200: return "OK";
			case 201: return "Created";
			case 202: return "Accepted";
			case 203: return "Non-Authoritative Information";
			case 204: return "No Content";
			case 205: return "Reset Content";
			case 206: return "Partial Content";
			case 207: return "Multi-Status";
			case 300: return "Multiple Choices";
			case 301: return "Moved Permanently";
			case 302: return "Found";
			case 303: return "See Other";
			case 304: return "Not Modified";
			case 305: return "Use Proxy";
			case 307: return "Temporary Redirect";
			case 400: return "Bad Request";
			case 401: return "Unauthorized";
			case 402: return "Payment Required";
			case 403: return "Forbidden";
			case 404: return "Not Found";
			case 405: return "Method Not Allowed";
			case 406: return "Not Acceptable";
			case 407: return "Proxy Authentication Required";
			case 408: return "Request Timeout";
			case 409: return "Conflict";
			case 410: return "Gone";
			case 411: return "Length Required";
			case 412: return "Precondition Failed";
			case 413: return "Request Entity Too Large";
			case 414: return "Request-Uri Too Long";
			case 415: return "Unsupported Media Type";
			case 416: return "Requested Range Not Satisfiable";
			case 417: return "Expectation Failed";
			case 422: return "Unprocessable Entity";
			case 423: return "Locked";
			case 424: return "Failed Dependency";
			case 500: return "Internal Server Error";
			case 501: return "Not Implemented";
			case 502: return "Bad Gateway";
			case 503: return "Service Unavailable";
			case 504: return "Gateway Timeout";
			case 505: return "Http Version Not Supported";
			case 507: return "Insufficient Storage";
			}
			return "";
		}
#endregion

#region Internals
		//
		// These are exposed on the public API, but are for internal consumption
		//
		public virtual byte [] GetClientCertificate ()
		{
			return new byte [0];
		}
		
		public virtual byte [] GetClientCertificateBinaryIssuer ()
		{
			return new byte [0];
		}
		
		public virtual int GetClientCertificateEncoding  ()
		{
			return 0;
		}
		
		public virtual byte [] GetClientCertificatePublicKey ()
		{
			return new byte [0];
		}

		public virtual DateTime GetClientCertificateValidFrom ()
		{
			return DateTime.Now;
		}

		public virtual DateTime GetClientCertificateValidUntil ()
		{
			return DateTime.Now;
		}

		public virtual long GetConnectionID ()
		{
			return 0;
		}

		public virtual long GetUrlContextID ()
		{
			return 0;
		}

		public virtual IntPtr GetVirtualPathToken ()
		{
			return IntPtr.Zero;
		}
#endregion
		
	}
}
