Merge pull request #2530 from lambdageek/monoerror-mono_string_new
[mono.git] / mcs / class / System.Web / System.Web / HttpServerUtility.cs
index 86c925de04b00a40fa4cd0bb0cbd04d59503e36f..9f94dcec23f85e2fef61c1f59fcbcdd426405967 100644 (file)
@@ -1,4 +1,14 @@
+//
+// System.Web.HttpRequest.cs 
+//
+// 
+// Author:
+//     Miguel de Icaza (miguel@novell.com)
+//     Gonzalo Paniagua Javier (gonzalo@novell.com)
+//
 
+//
+// Copyright (C) 2005-2010 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
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-/**
- * Namespace: System.Web
- * Class:     HttpServerUtility
- *
- * Authosr:
- *     Wictor Wilén (wictor@ibizkit.se)
- *     Patrik Torstensson (patrik.torstensson@labs2.com)
- *     Gonzalo Paniagua Javier (gonzalo@ximian.com)
- *
- * (c) Wictor Wilén (2002)
- * (c) 2002 Patrik Torstensson
- * (c) 2003 Ximian, Inc. (http://www.ximian.com)
- *
- * (This log is no longer maintained)
- * ---------------------------------------
- * 2002-03-27  Wictor      Started implementation
- * 2002-04-09  Patrik      Added HttpContext constructor
- * 2002-04-10  Patrik      Moved encoding to HttpUtility and
- *                         fixed all functions that used
- *                         HttpContext
- * 
- */
-using System;
-using System.Collections.Specialized;
+
 using System.IO;
-using System.Text;
-using System.Web.Hosting;
 using System.Web.UI;
 using System.Web.Util;
+using System.Collections.Specialized;
+using System.Security.Permissions;
+using System.Text;
+using System.Threading;
+using System.Web.Configuration;
+using System.Web.SessionState;
 
 namespace System.Web
 {
-       public sealed class HttpServerUtility
-       {
-               private static string _name;
-
-               private HttpContext _Context;
-               private HttpApplication _Application;
-
-               internal HttpServerUtility (HttpContext Context)
+       //
+       // Methods exposed through HttpContext.Server property
+       //
+       
+       // CAS - no InheritanceDemand here as the class is sealed
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       public sealed class HttpServerUtility {
+               HttpContext context;
+               
+               internal HttpServerUtility (HttpContext context)
                {
-                       _Context = Context;
-               }
-
-               internal HttpServerUtility (HttpApplication app)
-               {
-                       _Application = app;
-               }
-
-               // Properties
-
-
-               /// <summary>
-               /// Gets the server's computer name.
-               /// </summary>
-               public string MachineName {
-                       get {
-                               if(_name == null)
-                                       _name = Environment.MachineName;
-
-                               return _name;
-                       }
-               }
-
-               public int ScriptTimeout {
-                       get {
-                               return (int) _Context.ConfigTimeout.TotalSeconds;
-                       } 
-                       set {
-                               if (value <= 0)
-                                       throw new ArgumentOutOfRangeException ("value");
-
-                               _Context.ConfigTimeout = new TimeSpan (0, 0, value);
-                       }
+                       this.context = context;
                }
 
-               // Methods
-
-               /// <summary>
-               /// Clears the previous exception.
-               /// </summary>
                public void ClearError ()
                {
-                       if (null != _Context) {
-                               _Context.ClearError ();
-                               return;
-                       }
-
-                       if (null != _Application) {
-                               _Application.ClearError ();
-                       }
+                       context.ClearError ();
                }
 
-
-               /// <summary>
-               /// Creates a server instance of a COM object identified by the object's Programmatic Identifier (ProgID).
-               /// </summary>
-               /// <param name="progID">The class or type of object to be instantiated. </param>
-               /// <returns>The new object.</returns>
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public object CreateObject (string progID)
                {
-                       return CreateObject (Type.GetTypeFromProgID (progID));
+                       throw new HttpException (500, "COM is not supported");
                }
 
-
-               /// <summary>
-               /// Creates a server instance of a COM object identified by the object's type.
-               /// </summary>
-               /// <param name="type">A Type representing the object to create. </param>
-               /// <returns>The new object.</returns>
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public object CreateObject (Type type)
                {
-                       return Activator.CreateInstance (type);
+                       throw new HttpException (500, "COM is not supported");
                }
 
-
-               /// <summary>
-               /// Creates a server instance of a COM object identified by the object's class identifier (CLSID).
-               /// </summary>
-               /// <param name="clsid">The class identifier of the object to be instantiated. </param>
-               /// <returns>The new object.</returns>
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public object CreateObjectFromClsid (string clsid)
                {
-                       Guid guid = new Guid (clsid);
-                       return CreateObject (Type.GetTypeFromCLSID (guid));
+                       throw new HttpException (500, "COM is not supported");
                }
 
-
-               /// <summary>
-               /// Executes a request to another page using the specified URL path to the page.
-               /// </summary>
-               /// <param name="path">The URL path of the new request. </param>
                public void Execute (string path)
                {
-                       Execute (path, null);
+                       Execute (path, null, true);
                }
 
-
-               /// <summary>
-               /// Executes a request to another page using the specified URL path to the page.
-               /// A TextWriter captures output from the page.
-               /// </summary>
-               /// <param name="path">The URL path of the new request. </param>
-               /// <param name="writer">The TextWriter to capture the output. </param>
                public void Execute (string path, TextWriter writer)
                {
-                       Execute (path, writer, false);
+                       Execute (path, writer, true);
                }
 
-#if NET_2_0
-               public
-#else
-               internal
-#endif
-               void Execute (string path, TextWriter writer, bool preserveQuery)
+               public void Execute (string path, bool preserveForm)
+               {
+                       Execute (path, null, preserveForm);
+               }
+
+               public void Execute (string path, TextWriter writer, bool preserveForm)
+               {                       
+                       Execute (path, writer, preserveForm, false);
+               }
+
+               void Execute (string path, TextWriter writer, bool preserveForm, bool isTransfer)
                {
                        if (path == null)
                                throw new ArgumentNullException ("path");
 
-                       if (_Context == null)
-                               throw new HttpException ("No context available.");
-
                        if (path.IndexOf (':') != -1)
                                throw new ArgumentException ("Invalid path.");
 
+                       string queryString = null;
                        int qmark = path.IndexOf ('?');
-                       string query;
                        if (qmark != -1) {
-                               query = path.Substring (qmark + 1);
+                               queryString = path.Substring (qmark + 1);
                                path = path.Substring (0, qmark);
-                       } else {
-                               query = "";
                        }
 
-                       HttpRequest request = _Context.Request;
-                       HttpResponse response = _Context.Response;
+                       string exePath = UrlUtils.Combine (context.Request.BaseVirtualDir, path);
+                       bool cookieless = false;
+                       SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
+                       cookieless = SessionStateModule.IsCookieLess (context, config);
+                       
+                       if (cookieless)
+                               exePath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (exePath), exePath);
+                       
+                       IHttpHandler handler = context.ApplicationInstance.GetHandler (context, exePath, true);
+                       Execute (handler, writer, preserveForm, exePath, queryString, isTransfer, true);
+               }
+
+               internal void Execute (IHttpHandler handler, TextWriter writer, bool preserveForm, string exePath, string queryString, bool isTransfer, bool isInclude)
+               {
+                       // If the target handler is not Page, the transfer must not occur.
+                       // InTransit == true means we're being called from Transfer
+                       bool is_static = (handler is StaticFileHandler);
+                       if (isTransfer && !(handler is Page) && !is_static)
+                               throw new HttpException ("Transfer is only allowed to .aspx and static files");
 
+                       HttpRequest request = context.Request;
                        string oldQuery = request.QueryStringRaw;
-                       request.QueryStringRaw = query;
+                       if (queryString != null) {
+                               request.QueryStringRaw = queryString;
+                       } else if (!preserveForm) {
+                               request.QueryStringRaw = String.Empty;
+                       }
 
-                       HttpValueCollection oldForm = null;
-                       if (!preserveQuery) {
-                               oldForm = _Context.Request.Form as HttpValueCollection;
-                               _Context.Request.SetForm (new HttpValueCollection ());
+                       HttpResponse response = context.Response;
+                       WebROCollection oldForm = request.Form as WebROCollection;
+                       if (!preserveForm) {
+                               request.SetForm (new WebROCollection ());
                        }
 
-                       string filePath = _Context.Request.MapPath (path);
                        TextWriter output = writer;
                        if (output == null)
                                output = response.Output;
-
-                       string oldFilePath = request.FilePath;
-                       request.SetCurrentExePath (UrlUtils.Combine (_Context.Request.BaseVirtualDir, path));
-                       IHttpHandler handler = _Context.ApplicationInstance.CreateHttpHandler (_Context,
-                                                                                              request.RequestType,
-                                                                                              path,
-                                                                                              filePath);
-                       TextWriter previous = null;
+                       
+                       TextWriter previous = response.SetTextWriter (output);
+                       string oldExePath = request.CurrentExecutionFilePath;
+                       bool oldIsInclude = context.IsProcessingInclude;
                        try {
-                               previous = response.SetTextWriter (output);
+                               context.PushHandler (handler);
+                               if (is_static) // Not sure if this should apply to Page too
+                                       request.SetFilePath (exePath);
+
+                               request.SetCurrentExePath (exePath);
+                               context.IsProcessingInclude = isInclude;
+                               
                                if (!(handler is IHttpAsyncHandler)) {
-                                       handler.ProcessRequest (_Context);
+                                       handler.ProcessRequest (context);
                                } else {
                                        IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler;
-                                       IAsyncResult ar = asyncHandler.BeginProcessRequest (_Context, null, null);
-                                       ar.AsyncWaitHandle.WaitOne ();
+                                       IAsyncResult ar = asyncHandler.BeginProcessRequest (context, null, null);
+                                       WaitHandle asyncWaitHandle = ar != null ? ar.AsyncWaitHandle : null;
+                                       if (asyncWaitHandle != null)
+                                               asyncWaitHandle.WaitOne ();
                                        asyncHandler.EndProcessRequest (ar);
                                }
                        } finally {
-                               request.SetCurrentExePath (oldFilePath);
-                               request.QueryStringRaw = oldQuery;
+                               if (oldQuery != request.QueryStringRaw) {
+                                       if (oldQuery != null && oldQuery.Length > 0) {
+                                               oldQuery = oldQuery.Substring (1); // Ignore initial '?'
+                                               request.QueryStringRaw = oldQuery; // which is added here.
+                                       } else
+                                               request.QueryStringRaw = String.Empty;
+                               }
+                               
                                response.SetTextWriter (previous);
-                               if (!preserveQuery)
-                                       _Context.Request.SetForm (oldForm);
-                       }
+                               if (!preserveForm)
+                                       request.SetForm (oldForm);
 
+                               context.PopHandler ();
+                               request.SetCurrentExePath (oldExePath);
+                               context.IsProcessingInclude = oldIsInclude;
+                       }
                }
 
-               /// <summary>
-               /// Returns the previous exception.
-               /// </summary>
-               /// <returns>The previous exception that was thrown.</returns>
                public Exception GetLastError ()
                {
-                       if (_Context == null)
-                               return null;
-
-                       return _Context.Error;
+                       if (context == null)
+                               return HttpContext.Current.Error;
+                       return context.Error;
                }
 
-
-
-               /// <summary>
-               /// Decodes an HTML-encoded string and returns the decoded string.
-               /// </summary>
-               /// <param name="s">The HTML string to decode. </param>
-               /// <returns>The decoded text.</returns>
                public string HtmlDecode (string s)
                {
                        return HttpUtility.HtmlDecode (s);
                }
 
-
-               /// <summary>
-               /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
-               /// </summary>
-               /// <param name="s">The HTML string to decode</param>
-               /// <param name="output">The TextWriter output stream containing the decoded string. </param>
                public void HtmlDecode (string s, TextWriter output)
                {
-                       output.Write (HttpUtility.HtmlDecode (s));
+                       HttpUtility.HtmlDecode (s, output);
                }
 
-               /// <summary>
-               /// HTML-encodes a string and returns the encoded string.
-               /// </summary>
-               /// <param name="s">The text string to encode. </param>
-               /// <returns>The HTML-encoded text.</returns>
                public string HtmlEncode (string s)
                {
                        return HttpUtility.HtmlEncode (s);
                }
 
-               /// <summary>
-               /// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
-               /// </summary>
-               /// <param name="s">The string to encode. </param>
-               /// <param name="output">The TextWriter output stream containing the encoded string. </param>
                public void HtmlEncode (string s, TextWriter output)
                {
-                       output.Write (HtmlEncode (s));
+                       HttpUtility.HtmlEncode (s, output);
                }
 
-
-               /// <summary>
-               /// Returns the physical file path that corresponds to the specified virtual path on the Web server.
-               /// </summary>
-               /// <param name="path">The virtual path on the Web server. </param>
-               /// <returns>The physical file path that corresponds to path.</returns>
                public string MapPath (string path)
                {
-                       if (null == _Context)
-                               throw new HttpException ("MapPath is not available");
+                       return context.Request.MapPath (path);
+               }
 
-                       return _Context.Request.MapPath (path);
+               
+               public void TransferRequest (string path)
+               {
+                       TransferRequest (path, false, null, null);
+               }
+               
+               public void TransferRequest (string path, bool preserveForm)
+               {
+                       TransferRequest (path, preserveForm, null, null);
                }
 
-               /// <summary>
-               /// Terminates execution of the current page and begins execution of a new page using the specified
-               /// URL path to the page.
-               /// </summary>
-               /// <param name="path">The URL path of the new page on the server to execute. </param>
+               [MonoTODO ("Always throws PlatformNotSupportedException.")]
+               public void TransferRequest (string path, bool preserveForm, string method, NameValueCollection headers)
+               {
+                       throw new PlatformNotSupportedException ();
+               }
+               
                public void Transfer (string path)
                {
-                       // If it's a page and a postback, don't pass form data
-                       // See bug #65613.
-                       bool preserveForm = true;
-                       if (_Context.Handler is Page) {
-                               Page page = (Page) _Context.Handler;
-                               preserveForm = !page.IsPostBack;
-                       }
+                       Transfer (path, true);
+               }
 
-                       Transfer (path, preserveForm);
+               public void Transfer (string path, bool preserveForm) {
+                       Execute (path, null, preserveForm, true);
+                       context.Response.End ();
                }
 
-               /// <summary>
-               /// Terminates execution of the current page and begins execution of a new page using the specified
-               /// URL path to the page. Specifies whether to clear the QueryString and Form collections.
-               /// </summary>
-               /// <param name="path">The URL path of the new page on the server to execute. </param>
-               /// <param name="preserveForm">If true, the QueryString and Form collections are preserved. If false,
-               /// they are cleared. The default is false. </param>
-               public void Transfer (string path, bool preserveForm)
+               public void Transfer (IHttpHandler handler, bool preserveForm)
                {
-                       Execute (path, null, preserveForm);
-                       _Context.Response.End ();
-               }
-
-               /// <summary>
-               /// URL-decodes a string and returns the decoded string.
-               /// </summary>
-               /// <param name="s">The text string to decode. </param>
-               /// <returns>The decoded text.</returns>
-               /// <remarks>
-               /// Post/html encoding @ ftp://ftp.isi.edu/in-notes/rfc1866.txt
-               /// Uncomment the line marked with RFC1738 to get pure RFC1738
-               /// and it will also consider the RFC1866 (ftp://ftp.isi.edu/in-notes/rfc1866.txt)
-               /// `application/x-www-form-urlencoded' format
-               /// </remarks>
+                       if (handler == null)
+                               throw new ArgumentNullException ("handler");
+
+                       // TODO: see the MS doc and search for "enableViewStateMac": this method is not
+                       // allowed for pages when preserveForm is true and the page IsCallback property
+                       // is true.
+                       Execute (handler, null, preserveForm, context.Request.CurrentExecutionFilePath, null, true, true);
+                       context.Response.End ();
+               }
+
+               public void Execute (IHttpHandler handler, TextWriter writer, bool preserveForm)
+               {
+                       if (handler == null)
+                               throw new ArgumentNullException ("handler");
+
+                       Execute (handler, writer, preserveForm, context.Request.CurrentExecutionFilePath, null, false, true);
+               }
+
+               public static byte[] UrlTokenDecode (string input)
+               {
+                       if (input == null)
+                               throw new ArgumentNullException ("input");
+                       if (input.Length < 1)
+                               return new byte[0];
+                       byte[] bytes = Encoding.ASCII.GetBytes (input);
+                       int inputLength = input.Length - 1;
+                       int equalsCount = (int)(((char)bytes[inputLength]) - 0x30);
+                       char[] ret = new char[inputLength + equalsCount];
+                       int i = 0;
+                       for (; i < inputLength; i++) {
+                               switch ((char)bytes[i]) {
+                                       case '-':
+                                               ret[i] = '+';
+                                               break;
+
+                                       case '_':
+                                               ret[i] = '/';
+                                               break;
+
+                                       default:
+                                               ret[i] = (char)bytes[i];
+                                               break;
+                               }
+                       }
+                       while (equalsCount > 0) {
+                               ret[i++] = '=';
+                               equalsCount--;
+                       }
+                       
+                       return Convert.FromBase64CharArray (ret, 0, ret.Length);
+               }
+
+               public static string UrlTokenEncode (byte[] input)
+               {
+                       if (input == null)
+                               throw new ArgumentNullException ("input");
+                       if (input.Length < 1)
+                               return String.Empty;
+                       string base64 = Convert.ToBase64String (input);
+                       int retlen;
+                       if (base64 == null || (retlen = base64.Length) == 0)
+                               return String.Empty;
+
+                       // MS.NET implementation seems to process the base64
+                       // string before returning. It replaces the chars:
+                       //
+                       //  + with -
+                       //  / with _
+                       //
+                       // Then removes trailing ==, which may appear in the
+                       // base64 string, and replaces them with a single digit
+                       // that's the count of removed '=' characters (0 if none
+                       // were removed)
+                       int equalsCount = 0x30;
+                       while (retlen > 0 && base64[retlen - 1] == '=') {
+                               equalsCount++;
+                               retlen--;
+                       }
+                       char[] chars = new char[retlen + 1];
+                       chars[retlen] = (char)equalsCount;
+                       for (int i = 0; i < retlen; i++) {
+                               switch (base64[i]) {
+                                       case '+':
+                                               chars[i] = '-';
+                                               break;
+
+                                       case '/':
+                                               chars[i] = '_';
+                                               break;
+                                       
+                                       default:
+                                               chars[i] = base64[i];
+                                               break;
+                               }
+                       }
+                       return new string (chars);
+               }
+
                public string UrlDecode (string s)
                {
-                       return HttpUtility.UrlDecode (s);
+                       HttpRequest request = context.Request;
+                       if(request != null)
+                               return HttpUtility.UrlDecode (s, request.ContentEncoding);
+                       else
+                               return HttpUtility.UrlDecode (s);
                }
 
-               /// <summary>
-               /// Decodes an HTML string received in a URL and sends the resulting output to a TextWriter output stream.
-               /// </summary>
-               /// <param name="s"></param>
-               /// <param name="output"></param>
                public void UrlDecode (string s, TextWriter output)
                {
                        if (s != null)
                                output.Write (UrlDecode (s));
                }
 
-               /// <summary>
-               /// URL-encodes a string and returns the encoded string.
-               /// </summary>
-               /// <param name="s">The text to URL-encode. </param>
-               /// <returns>The URL encoded text.</returns>
                public string UrlEncode (string s)
                {
-                       return HttpUtility.UrlEncode (s);
+                       HttpResponse response = context.Response;
+                       if (response != null)
+                               return HttpUtility.UrlEncode (s, response.ContentEncoding);
+                       else
+                               return HttpUtility.UrlEncode (s);
                }
 
-               /// <summary>
-               /// URL encodes a string and sends the resulting output to a TextWriter output stream.
-               /// </summary>
-               /// <param name="s">The text string to encode. </param>
-               /// <param name="output">The TextWriter output stream containing the encoded string. </param>
                public void UrlEncode (string s, TextWriter output)
                {
                        if (s != null)
                                output.Write (UrlEncode (s));
                }
 
-               /// <summary>
-               /// URL-encodes the path portion of a URL string and returns the encoded string.
-               /// </summary>
-               /// <param name="s">The text to URL-encode.</param>
-               /// <returns>The URL encoded text.</returns>
-               /// <remarks>Does not do any browser specific adjustments, just encode everything</remarks>
                public string UrlPathEncode (string s)
                {
                        if (s == null)
                                return null;
 
-                       int idx = s.IndexOf ("?");
+                       int idx = s.IndexOf ('?');
                        string s2 = null;
                        if (idx != -1) {
-                               s2 = s.Substring (0, idx-1);
-                               s2 = UrlEncode (s2) + s.Substring (idx);
+                               s2 = s.Substring (0, idx);
+                               s2 = HttpUtility.UrlEncode (s2) + s.Substring (idx);
                        } else {
-                               s2 = UrlEncode (s);
+                               s2 = HttpUtility.UrlEncode (s);
                        }
 
                        return s2;
                }
+
+               public string MachineName {
+                       [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
+                       // Medium doesn't look heavy enough to replace this... reported as
+                       [SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
+                       [EnvironmentPermission (SecurityAction.Assert, Read = "COMPUTERNAME")]
+                       get { return Environment.MachineName; }
+               }
+
+               public int ScriptTimeout {
+                       get { return (int) context.ConfigTimeout.TotalSeconds; }
+                       [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
+                       set { context.ConfigTimeout = TimeSpan.FromSeconds (value); }
+               }
        }
 }