// 
// System.Web.Hosting.SimpleWorkerRequest.cs
//
// Authors:
// 	Patrik Torstensson (Patrik.Torstensson@labs2.com)
// 	(class signature from Bob Smith <bob@thestuff.net> (C) )
// 	Gonzalo Paniagua Javier (gonzalo@ximian.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;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System.Web.Util;

namespace System.Web.Hosting
{
	[MonoTODO("Implement security demands on the path usage functions (and review)")]
	[ComVisible (false)]
	public class SimpleWorkerRequest : HttpWorkerRequest
	{
		private string _Page;
		private string _Query;
		private string _PathInfo = String.Empty;
		private string _AppVirtualPath;
		private string _AppPhysicalPath;
		private string _AppInstallPath;
		private TextWriter _Output;
		private bool _HasInstallInfo;

		private SimpleWorkerRequest ()
		{
		}

		public SimpleWorkerRequest (string Page, string Query, TextWriter Output)
		{
			_Page = Page;

			_Query = Query;
			AppDomain current = AppDomain.CurrentDomain;
			object o = current.GetData (".appPath");
			if (o == null)
				throw new HttpException ("Cannot get .appPath");
			_AppPhysicalPath = o.ToString ();

			o = current.GetData (".hostingVirtualPath");
			if (o == null)
				throw new HttpException ("Cannot get .hostingVirtualPath");
			_AppVirtualPath = o.ToString ();

			o = current.GetData (".hostingInstallDir");
			if (o == null)
				throw new HttpException ("Cannot get .hostingInstallDir");
			_AppInstallPath = o.ToString ();
			_Output = Output;

			if (_AppPhysicalPath == null)
				throw new HttpException ("Invalid app domain");

			_HasInstallInfo = true;

			ExtractPagePathInfo();
		}

		public SimpleWorkerRequest (string AppVirtualPath,
					    string AppPhysicalPath,
					    string Page,
					    string Query,
					    TextWriter Output)
		{
			if (AppDomain.CurrentDomain.GetData (".appPath") == null)
				throw new HttpException ("Invalid app domain");

			_Page = Page;
			_Query = Query;
			_AppVirtualPath = AppVirtualPath;
			_AppPhysicalPath = CheckAndAddSlash (AppPhysicalPath);
			_Output = Output;
			_HasInstallInfo = false;

			ExtractPagePathInfo();
		}

		[MonoTODO("Implement security")]
		public override string MachineInstallDirectory
		{
			get {
				if (_HasInstallInfo)
					return _AppInstallPath;

				return ICalls.GetMachineInstallDirectory ();
			}
		}

		public override string MachineConfigPath
		{
			get { return ICalls.GetMachineConfigPath (); }
		}

		public override void EndOfRequest ()
		{
		}

		public override void FlushResponse (bool finalFlush)
		{
		}

		public override string GetAppPath ()
		{
			return _AppVirtualPath;
		}

		public override string GetAppPathTranslated ()
		{
			return _AppPhysicalPath;
		}

		public override string GetFilePath ()
		{
                        return CreatePath (false);
		}

		public override string GetFilePathTranslated ()
		{
                        string page = _Page;

			if (Path.DirectorySeparatorChar != '/')
                                page = _Page.Replace ('/', Path.DirectorySeparatorChar);

			if (page [0] == Path.DirectorySeparatorChar)
				page = page.Substring (1);
			
			return (Path.Combine (_AppPhysicalPath, page));
		}

		public override string GetHttpVerbName ()
		{
			return "GET";
		}

		public override string GetHttpVersion ()
		{
			return "HTTP/1.0";
		}

		public override string GetLocalAddress ()
		{
			return "127.0.0.1";
		}

		public override int GetLocalPort ()
		{
			return 80;
		}

		public override string GetPathInfo ()
		{
			return _PathInfo;
		}

		public override string GetQueryString ()
		{
			return _Query;
		}

		public override string GetRawUrl ()
		{
                        string path = CreatePath (true);
                        if (null != _Query && _Query.Length > 0)
				return path + "?" + _Query;

			return path;
		}

		public override string GetRemoteAddress()
		{
			return "127.0.0.1";
		}

		public override int GetRemotePort()
		{
			return 0;
		}

		public override string GetServerVariable(string name)
		{
			return String.Empty;
		}

		public override string GetUriPath()
		{
			return CreatePath (true);
		}

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

		public override string MapPath (string path)
		{
			string sPath = _AppPhysicalPath.Substring (0, _AppPhysicalPath.Length - 1);
			if (path != null && path.Length > 0 && path [0] != '/')
				return sPath;
 
			char sep = Path.DirectorySeparatorChar;
			if (path.StartsWith(_AppVirtualPath)) {
				if (sep == '/')
					return sPath + path.Substring (_AppVirtualPath.Length);
				else
					return sPath + path.Substring (_AppVirtualPath.Length).Replace ('/', sep);
			}

			return null;
		}

		public override void SendKnownResponseHeader (int index, string value)
		{
		}

		public override void SendResponseFromFile (IntPtr handle, long offset, long length)
		{
		}

		public override void SendResponseFromFile (string filename, long offset, long length)
		{
		}

		public override void SendResponseFromMemory (byte [] data, int length)
		{
			_Output.Write (Encoding.Default.GetChars (data, 0, length));
		}

		public override void SendStatus(int statusCode, string statusDescription)
		{
		}

		public override void SendUnknownResponseHeader(string name, string value)
		{
		}

		// Create's a path string
		private string CheckAndAddSlash(string sPath)
		{
			if (null == sPath)
				return null;

			if (!sPath.EndsWith ("" + Path.DirectorySeparatorChar))
				return sPath + Path.DirectorySeparatorChar;

			return sPath;
		}

		// Create's a path string
                private string CreatePath (bool bIncludePathInfo)
                {
                        string sPath = Path.Combine (_AppVirtualPath, _Page);

                        if (bIncludePathInfo)
                        {
                                sPath += _PathInfo;
                        }

                        return sPath;
		}

		//  "The extra path information, as given by the client. In
		//  other words, scripts can be accessed by their virtual
		//  pathname, followed by extra information at the end of this
		//  path. The extra information is sent as PATH_INFO."
		private void ExtractPagePathInfo ()
		{
			if (_Page == null || _Page == String.Empty)
				return;

			string FullPath = GetFilePathTranslated ();
			int PathInfoLength = 0;
			string LastFile = String.Empty;

			while (PathInfoLength < _Page.Length) {
				if (LastFile.Length > 0) {
					// increase it by the length of the file plus 
					// a "/"
					//
					PathInfoLength += LastFile.Length + 1;
				}

				if (File.Exists (FullPath) == true)
					break;

				if (Directory.Exists (FullPath) == true) {
					PathInfoLength -= (LastFile.Length + 1);
					break;
				}

				LastFile = Path.GetFileName (FullPath);
				FullPath = Path.GetDirectoryName (FullPath);
			}

			if (PathInfoLength <= 0 || PathInfoLength > _Page.Length)
				return;

			_PathInfo = _Page.Substring (_Page.Length - PathInfoLength);
			_Page = _Page.Substring (0, _Page.Length - PathInfoLength);
		}
	}
}

