initial implementation of 'unify request'
[mono.git] / mcs / class / System.Web / System.Web / HttpServerUtility.cs
1 //
2 // System.Web.HttpRequest.cs 
3 //
4 // 
5 // Author:
6 //      Miguel de Icaza (miguel@novell.com)
7 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
8 //
9
10 //
11 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System.IO;
34 using System.Web.UI;
35 using System.Web.Util;
36 using System.Collections.Specialized;
37 using System.Security.Permissions;
38 using System.Text;
39
40 namespace System.Web {
41
42         //
43         // Methods exposed through HttpContext.Server property
44         //
45         
46         // CAS - no InheritanceDemand here as the class is sealed
47         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48         public sealed class HttpServerUtility {
49                 HttpContext context;
50                 
51                 internal HttpServerUtility (HttpContext context)
52                 {
53                         this.context = context;
54                 }
55
56                 public void ClearError ()
57                 {
58                         context.ClearError ();
59                 }
60
61                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
62                 public object CreateObject (string progID)
63                 {
64                         throw new HttpException (500, "COM is not supported");
65                 }
66
67                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
68                 public object CreateObject (Type type)
69                 {
70                         throw new HttpException (500, "COM is not supported");
71                 }
72
73                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
74                 public object CreateObjectFromClsid (string clsid)
75                 {
76                         throw new HttpException (500, "COM is not supported");
77                 }
78
79                 public void Execute (string path)
80                 {
81                         Execute (path, null, true);
82                 }
83
84                 public void Execute (string path, TextWriter writer)
85                 {
86                         Execute (path, writer, true);
87                 }
88
89 #if NET_2_0
90                 public void Execute (string path, bool preserveForm)
91                 {
92                         Execute (path, null, preserveForm);
93                 }
94 #endif
95                 
96 #if NET_2_0
97                 public
98 #else
99                 internal
100 #endif
101                 void Execute (string path, TextWriter writer, bool preserveForm)
102                 {                       
103                         if (path == null)
104                                 throw new ArgumentNullException ("path");
105
106                         if (path.IndexOf (':') != -1)
107                                 throw new ArgumentException ("Invalid path.");
108
109                         HttpRequest request = context.Request;
110                         string oldQuery = request.QueryStringRaw;
111                         int qmark = path.IndexOf ('?');
112                         if (qmark != -1) {
113                                 request.QueryStringRaw = path.Substring (qmark + 1);
114                                 path = path.Substring (0, qmark);
115                         } else if (!preserveForm) {
116                                 request.QueryStringRaw = "";
117                         }
118
119                         HttpResponse response = context.Response;
120                         WebROCollection oldForm = null;
121                         if (!preserveForm) {
122                                 oldForm = request.Form as WebROCollection;
123                                 request.SetForm (new WebROCollection ());
124                         }
125
126                         TextWriter output = writer;
127                         if (output == null)
128                                 output = response.Output;
129
130                         string oldFilePath = request.FilePath;
131                         request.SetCurrentExePath (UrlUtils.Combine (request.BaseVirtualDir, path));
132                         IHttpHandler handler = context.ApplicationInstance.GetHandler (context);
133                         request.SetCurrentExePath (oldFilePath);
134                         
135 #if NET_2_0
136                         // If the target handler is not Page, the transfer must not occur.
137                         // InTransit == true means we're being called from Transfer
138                         if (context.InTransit && !(handler is Page))
139                                 throw new HttpException ("Transfer is possible only to .aspx files");
140 #endif
141                         
142                         TextWriter previous = null;
143                         try {
144 #if NET_2_0
145                                 context.PushHandler (handler);
146 #endif
147                                 previous = response.SetTextWriter (output);
148                                 
149                                 if (!(handler is IHttpAsyncHandler)) {
150                                         handler.ProcessRequest (context);
151                                 } else {
152                                         IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler;
153                                         IAsyncResult ar = asyncHandler.BeginProcessRequest (context, null, null);
154                                         ar.AsyncWaitHandle.WaitOne ();
155                                         asyncHandler.EndProcessRequest (ar);
156                                 }
157                         } finally {
158                                 if (oldQuery != null && oldQuery != "" && oldQuery != request.QueryStringRaw) {
159                                         oldQuery = oldQuery.Substring (1); // Ignore initial '?'
160                                         request.QueryStringRaw = oldQuery; // which is added here.
161                                 }
162                                 response.SetTextWriter (previous);
163                                 if (!preserveForm)
164                                         request.SetForm (oldForm);
165                                 context.InTransit = false;
166 #if NET_2_0
167                                 context.PopHandler ();
168 #endif
169                         }
170                 }
171
172                 public Exception GetLastError ()
173                 {
174                         if (context == null)
175                                 return HttpContext.Current.Error;
176                         return context.Error;
177                 }
178
179                 public string HtmlDecode (string s)
180                 {
181                         return HttpUtility.HtmlDecode (s);
182                 }
183
184                 public void HtmlDecode (string s, TextWriter output)
185                 {
186                         HttpUtility.HtmlDecode (s, output);
187                 }
188
189                 public string HtmlEncode (string s)
190                 {
191                         return HttpUtility.HtmlEncode (s);
192                 }
193
194                 public void HtmlEncode (string s, TextWriter output)
195                 {
196                         HttpUtility.HtmlEncode (s, output);
197                 }
198
199                 public string MapPath (string path)
200                 {
201                         return context.Request.MapPath (path);
202                 }
203
204                 public void Transfer (string path)
205                 {
206                         // If it's a page and a postback, don't pass form data
207                         // See bug #65613.
208                         bool preserveForm = true;
209                         if (context.Handler is Page) {
210                                 Page page = (Page) context.Handler;
211                                 preserveForm = !page.IsPostBack;
212                         }
213 #if NET_2_0
214                         else
215                                 throw new HttpException ("Transfer may only be called from within a Page instance");
216 #endif
217
218                         Transfer (path, preserveForm);
219                 }
220
221                 public void Transfer (string path, bool preserveForm)
222                 {
223 #if NET_2_0
224                         if (!(context.Handler is Page))
225                                 throw new HttpException ("Transfer may only be called from within a Page instance");
226 #endif
227
228                         context.InTransit = true;
229                         Execute (path, null, preserveForm);
230                         context.Response.End ();
231                 }
232 #if NET_2_0
233                 public void Transfer (IHttpHandler handler, bool preserveForm)
234                 {
235                         if (handler == null)
236                                 throw new ArgumentNullException ("handler");
237                         if (!(handler is Page))
238                                 throw new HttpException ("Transfer may only be called from within a Page instance");
239                         
240                         // TODO: see the MS doc and search for "enableViewStateMac": this method is not
241                         // allowed for pages when preserveForm is true and the page IsCallback property
242                         // is true.
243
244                         Execute (handler, null, preserveForm);
245                         context.Response.End ();
246                 }
247
248                 public void Execute (IHttpHandler handler, TextWriter writer, bool preserveForm)
249                 {
250                         if (handler == null)
251                                 throw new ArgumentNullException ("handler");
252
253                         // If the target handler is not Page, the transfer must not occur.
254                         // InTransit == true means we're being called from Transfer
255                         if (context.InTransit && !(handler is Page))
256                                 throw new HttpException ("Transfer is possible only to .aspx files");
257                         
258                         HttpRequest request = context.Request;
259                         string oldQuery = request.QueryStringRaw;
260                         if (!preserveForm) {
261                                 request.QueryStringRaw = "";
262                         }
263
264                         HttpResponse response = context.Response;
265                         WebROCollection oldForm = null;
266                         if (!preserveForm) {
267                                 oldForm = request.Form as WebROCollection;
268                                 request.SetForm (new WebROCollection ());
269                         }
270
271                         TextWriter output = writer;
272                         if (output == null)
273                                 output = response.Output;
274
275                         TextWriter previous = null;
276                         try {
277                                 previous = response.SetTextWriter (output);
278                                 if (!(handler is IHttpAsyncHandler)) {
279                                         handler.ProcessRequest (context);
280                                 } else {
281                                         IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler;
282                                         IAsyncResult ar = asyncHandler.BeginProcessRequest (context, null, null);
283                                         ar.AsyncWaitHandle.WaitOne ();
284                                         asyncHandler.EndProcessRequest (ar);
285                                 }
286                         } finally {
287                                 if (oldQuery != null && oldQuery != "" && oldQuery != request.QueryStringRaw) {
288                                         oldQuery = oldQuery.Substring (1); // Ignore initial '?'
289                                         request.QueryStringRaw = oldQuery; // which is added here.
290                                 }
291                                 response.SetTextWriter (previous);
292                                 if (!preserveForm)
293                                         request.SetForm (oldForm);
294                         }
295                 }
296
297                 public static byte[] UrlTokenDecode (string input)
298                 {
299                         if (input == null)
300                                 throw new ArgumentNullException ("input");
301                         if (input.Length < 1)
302                                 return new byte[0];
303                         byte[] bytes = Encoding.ASCII.GetBytes (input);
304                         int inputLength = input.Length - 1;
305                         int equalsCount = (int)(((char)bytes[inputLength]) - 0x30);
306                         char[] ret = new char[inputLength + equalsCount];
307                         int i = 0;
308                         for (; i < inputLength; i++) {
309                                 switch ((char)bytes[i]) {
310                                         case '-':
311                                                 ret[i] = '+';
312                                                 break;
313
314                                         case '_':
315                                                 ret[i] = '/';
316                                                 break;
317
318                                         default:
319                                                 ret[i] = (char)bytes[i];
320                                                 break;
321                                 }
322                         }
323                         while (equalsCount > 0) {
324                                 ret[i++] = '=';
325                                 equalsCount--;
326                         }
327                         
328                         return Convert.FromBase64CharArray (ret, 0, ret.Length);
329                 }
330
331                 public static string UrlTokenEncode (byte[] input)
332                 {
333                         if (input == null)
334                                 throw new ArgumentNullException ("input");
335                         if (input.Length < 1)
336                                 return String.Empty;
337                         string base64 = Convert.ToBase64String (input);
338                         int retlen;
339                         if (base64 == null || (retlen = base64.Length) == 0)
340                                 return String.Empty;
341
342                         // MS.NET implementation seems to process the base64
343                         // string before returning. It replaces the chars:
344                         //
345                         //  + with -
346                         //  / with _
347                         //
348                         // Then removes trailing ==, which may appear in the
349                         // base64 string, and replaces them with a single digit
350                         // that's the count of removed '=' characters (0 if none
351                         // were removed)
352                         int equalsCount = 0x30;
353                         while (retlen > 0 && base64[retlen - 1] == '=') {
354                                 equalsCount++;
355                                 retlen--;
356                         }
357                         char[] chars = new char[retlen + 1];
358                         chars[retlen] = (char)equalsCount;
359                         for (int i = 0; i < retlen; i++) {
360                                 switch (base64[i]) {
361                                         case '+':
362                                                 chars[i] = '-';
363                                                 break;
364
365                                         case '/':
366                                                 chars[i] = '_';
367                                                 break;
368                                         
369                                         default:
370                                                 chars[i] = base64[i];
371                                                 break;
372                                 }
373                         }
374                         return new string (chars);
375                 }
376 #endif
377
378                 public string UrlDecode (string s)
379                 {
380                         return HttpUtility.UrlDecode (s);
381                 }
382
383                 public void UrlDecode (string s, TextWriter output)
384                 {
385                         if (s != null)
386                                 output.Write (HttpUtility.UrlDecode (s));
387                 }
388
389                 public string UrlEncode (string s)
390                 {
391                         return HttpUtility.UrlEncode (s);
392                 }
393
394                 public void UrlEncode (string s, TextWriter output)
395                 {
396                         if (s != null)
397                                 output.Write (HttpUtility.UrlEncode (s));
398                 }
399
400                 public string UrlPathEncode (string s)
401                 {
402                         if (s == null)
403                                 return null;
404
405                         int idx = s.IndexOf ("?");
406                         string s2 = null;
407                         if (idx != -1) {
408                                 s2 = s.Substring (0, idx-1);
409                                 s2 = HttpUtility.UrlEncode (s2) + s.Substring (idx);
410                         } else {
411                                 s2 = HttpUtility.UrlEncode (s);
412                         }
413
414                         return s2;
415                 }
416
417                 public string MachineName {
418                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
419                         // Medium doesn't look heavy enough to replace this... reported as
420                         [SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
421                         [EnvironmentPermission (SecurityAction.Assert, Read = "COMPUTERNAME")]
422                         get { return Environment.MachineName; }
423                 }
424
425                 public int ScriptTimeout {
426                         get { return (int) context.ConfigTimeout.TotalSeconds; }
427                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
428                         set { context.ConfigTimeout = new TimeSpan (0, 0, value); }
429                 }
430         }
431 }