initial implementation of 'unify request'
[mono.git] / mcs / class / System.Web / System.Web / HttpServerUtility.cs
index 1205e177435008a7df821de0701e088e6021a5ab..819c47dba6ffe83fa63ae2ec127eb6b3bb300238 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.IO;
 using System.Web.UI;
 using System.Web.Util;
 using System.Collections.Specialized;
+using System.Security.Permissions;
+using System.Text;
 
 namespace System.Web {
 
@@ -42,6 +43,8 @@ namespace System.Web {
        // 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;
                
@@ -55,16 +58,19 @@ namespace System.Web {
                        context.ClearError ();
                }
 
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public object CreateObject (string progID)
                {
                        throw new HttpException (500, "COM is not supported");
                }
 
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public object CreateObject (Type type)
                {
                        throw new HttpException (500, "COM is not supported");
                }
 
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
                public object CreateObjectFromClsid (string clsid)
                {
                        throw new HttpException (500, "COM is not supported");
@@ -72,44 +78,47 @@ namespace System.Web {
 
                public void Execute (string path)
                {
-                       Execute (path, null);
+                       Execute (path, null, true);
                }
 
                public void Execute (string path, TextWriter writer)
                {
-                       Execute (path, writer, false);
+                       Execute (path, writer, true);
                }
 
+#if NET_2_0
+               public void Execute (string path, bool preserveForm)
+               {
+                       Execute (path, null, preserveForm);
+               }
+#endif
+               
 #if NET_2_0
                public
 #else
                internal
 #endif
-               void Execute (string path, TextWriter writer, bool preserveQuery)
-               {
+               void Execute (string path, TextWriter writer, bool preserveForm)
+               {                       
                        if (path == null)
                                throw new ArgumentNullException ("path");
 
                        if (path.IndexOf (':') != -1)
                                throw new ArgumentException ("Invalid path.");
 
+                       HttpRequest request = context.Request;
+                       string oldQuery = request.QueryStringRaw;
                        int qmark = path.IndexOf ('?');
-                       string query;
                        if (qmark != -1) {
-                               query = path.Substring (qmark + 1);
+                               request.QueryStringRaw = path.Substring (qmark + 1);
                                path = path.Substring (0, qmark);
-                       } else {
-                               query = "";
+                       } else if (!preserveForm) {
+                               request.QueryStringRaw = "";
                        }
 
-                       HttpRequest request = context.Request;
                        HttpResponse response = context.Response;
-
-                       string oldQuery = request.QueryStringRaw;
-                       request.QueryStringRaw = query;
-
                        WebROCollection oldForm = null;
-                       if (!preserveQuery) {
+                       if (!preserveForm) {
                                oldForm = request.Form as WebROCollection;
                                request.SetForm (new WebROCollection ());
                        }
@@ -121,9 +130,22 @@ namespace System.Web {
                        string oldFilePath = request.FilePath;
                        request.SetCurrentExePath (UrlUtils.Combine (request.BaseVirtualDir, path));
                        IHttpHandler handler = context.ApplicationInstance.GetHandler (context);
+                       request.SetCurrentExePath (oldFilePath);
+                       
+#if NET_2_0
+                       // If the target handler is not Page, the transfer must not occur.
+                       // InTransit == true means we're being called from Transfer
+                       if (context.InTransit && !(handler is Page))
+                               throw new HttpException ("Transfer is possible only to .aspx files");
+#endif
+                       
                        TextWriter previous = null;
                        try {
+#if NET_2_0
+                               context.PushHandler (handler);
+#endif
                                previous = response.SetTextWriter (output);
+                               
                                if (!(handler is IHttpAsyncHandler)) {
                                        handler.ProcessRequest (context);
                                } else {
@@ -133,16 +155,24 @@ namespace System.Web {
                                        asyncHandler.EndProcessRequest (ar);
                                }
                        } finally {
-                               request.SetCurrentExePath (oldFilePath);
-                               request.QueryStringRaw = oldQuery;
+                               if (oldQuery != null && oldQuery != "" && oldQuery != request.QueryStringRaw) {
+                                       oldQuery = oldQuery.Substring (1); // Ignore initial '?'
+                                       request.QueryStringRaw = oldQuery; // which is added here.
+                               }
                                response.SetTextWriter (previous);
-                               if (!preserveQuery)
+                               if (!preserveForm)
                                        request.SetForm (oldForm);
+                               context.InTransit = false;
+#if NET_2_0
+                               context.PopHandler ();
+#endif
                        }
                }
 
                public Exception GetLastError ()
                {
+                       if (context == null)
+                               return HttpContext.Current.Error;
                        return context.Error;
                }
 
@@ -180,15 +210,170 @@ namespace System.Web {
                                Page page = (Page) context.Handler;
                                preserveForm = !page.IsPostBack;
                        }
+#if NET_2_0
+                       else
+                               throw new HttpException ("Transfer may only be called from within a Page instance");
+#endif
 
                        Transfer (path, preserveForm);
                }
 
                public void Transfer (string path, bool preserveForm)
                {
+#if NET_2_0
+                       if (!(context.Handler is Page))
+                               throw new HttpException ("Transfer may only be called from within a Page instance");
+#endif
+
+                       context.InTransit = true;
                        Execute (path, null, preserveForm);
                        context.Response.End ();
                }
+#if NET_2_0
+               public void Transfer (IHttpHandler handler, bool preserveForm)
+               {
+                       if (handler == null)
+                               throw new ArgumentNullException ("handler");
+                       if (!(handler is Page))
+                               throw new HttpException ("Transfer may only be called from within a Page instance");
+                       
+                       // 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.Response.End ();
+               }
+
+               public void Execute (IHttpHandler handler, TextWriter writer, bool preserveForm)
+               {
+                       if (handler == null)
+                               throw new ArgumentNullException ("handler");
+
+                       // If the target handler is not Page, the transfer must not occur.
+                       // InTransit == true means we're being called from Transfer
+                       if (context.InTransit && !(handler is Page))
+                               throw new HttpException ("Transfer is possible only to .aspx files");
+                       
+                       HttpRequest request = context.Request;
+                       string oldQuery = request.QueryStringRaw;
+                       if (!preserveForm) {
+                               request.QueryStringRaw = "";
+                       }
+
+                       HttpResponse response = context.Response;
+                       WebROCollection oldForm = null;
+                       if (!preserveForm) {
+                               oldForm = request.Form as WebROCollection;
+                               request.SetForm (new WebROCollection ());
+                       }
+
+                       TextWriter output = writer;
+                       if (output == null)
+                               output = response.Output;
+
+                       TextWriter previous = null;
+                       try {
+                               previous = response.SetTextWriter (output);
+                               if (!(handler is IHttpAsyncHandler)) {
+                                       handler.ProcessRequest (context);
+                               } else {
+                                       IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler;
+                                       IAsyncResult ar = asyncHandler.BeginProcessRequest (context, null, null);
+                                       ar.AsyncWaitHandle.WaitOne ();
+                                       asyncHandler.EndProcessRequest (ar);
+                               }
+                       } finally {
+                               if (oldQuery != null && oldQuery != "" && oldQuery != request.QueryStringRaw) {
+                                       oldQuery = oldQuery.Substring (1); // Ignore initial '?'
+                                       request.QueryStringRaw = oldQuery; // which is added here.
+                               }
+                               response.SetTextWriter (previous);
+                               if (!preserveForm)
+                                       request.SetForm (oldForm);
+                       }
+               }
+
+               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);
+               }
+#endif
 
                public string UrlDecode (string s)
                {
@@ -230,13 +415,17 @@ namespace System.Web {
                }
 
                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 = new TimeSpan (0, 0, value); }
                }
        }
 }
-