2010-06-30 Atsushi Enomoto <atsushi@ximian.com>
[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-2010 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 using System.Threading;
40 using System.Web.Configuration;
41 using System.Web.SessionState;
42
43 namespace System.Web
44 {
45         //
46         // Methods exposed through HttpContext.Server property
47         //
48         
49         // CAS - no InheritanceDemand here as the class is sealed
50         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
51         public sealed class HttpServerUtility {
52                 HttpContext context;
53                 
54                 internal HttpServerUtility (HttpContext context)
55                 {
56                         this.context = context;
57                 }
58
59                 public void ClearError ()
60                 {
61                         context.ClearError ();
62                 }
63
64                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
65                 public object CreateObject (string progID)
66                 {
67                         throw new HttpException (500, "COM is not supported");
68                 }
69
70                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
71                 public object CreateObject (Type type)
72                 {
73                         throw new HttpException (500, "COM is not supported");
74                 }
75
76                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
77                 public object CreateObjectFromClsid (string clsid)
78                 {
79                         throw new HttpException (500, "COM is not supported");
80                 }
81
82                 public void Execute (string path)
83                 {
84                         Execute (path, null, true);
85                 }
86
87                 public void Execute (string path, TextWriter writer)
88                 {
89                         Execute (path, writer, true);
90                 }
91
92                 public void Execute (string path, bool preserveForm)
93                 {
94                         Execute (path, null, preserveForm);
95                 }
96
97                 public void Execute (string path, TextWriter writer, bool preserveForm)
98                 {                       
99                         Execute (path, writer, preserveForm, false);
100                 }
101
102                 void Execute (string path, TextWriter writer, bool preserveForm, bool isTransfer)
103                 {
104                         if (path == null)
105                                 throw new ArgumentNullException ("path");
106
107                         if (path.IndexOf (':') != -1)
108                                 throw new ArgumentException ("Invalid path.");
109
110                         string queryString = null;
111                         int qmark = path.IndexOf ('?');
112                         if (qmark != -1) {
113                                 queryString = path.Substring (qmark + 1);
114                                 path = path.Substring (0, qmark);
115                         }
116
117                         string exePath = UrlUtils.Combine (context.Request.BaseVirtualDir, path);
118                         bool cookieless = false;
119                         SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
120                         cookieless = SessionStateModule.IsCookieLess (context, config);
121                         
122                         if (cookieless)
123                                 exePath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (exePath), exePath);
124                         
125                         IHttpHandler handler = context.ApplicationInstance.GetHandler (context, exePath, true);
126                         Execute (handler, writer, preserveForm, exePath, queryString, isTransfer, true);
127                 }
128
129                 internal void Execute (IHttpHandler handler, TextWriter writer, bool preserveForm, string exePath, string queryString, bool isTransfer, bool isInclude)
130                 {
131 #if !TARGET_J2EE
132                         // If the target handler is not Page, the transfer must not occur.
133                         // InTransit == true means we're being called from Transfer
134                         bool is_static = (handler is StaticFileHandler);
135                         if (isTransfer && !(handler is Page) && !is_static)
136                                 throw new HttpException ("Transfer is only allowed to .aspx and static files");
137 #endif
138
139                         HttpRequest request = context.Request;
140                         string oldQuery = request.QueryStringRaw;
141                         if (queryString != null) {
142                                 request.QueryStringRaw = queryString;
143                         } else if (!preserveForm) {
144                                 request.QueryStringRaw = String.Empty;
145                         }
146
147                         HttpResponse response = context.Response;
148                         WebROCollection oldForm = request.Form as WebROCollection;
149                         if (!preserveForm) {
150                                 request.SetForm (new WebROCollection ());
151                         }
152
153                         TextWriter output = writer;
154                         if (output == null)
155                                 output = response.Output;
156                         
157                         TextWriter previous = response.SetTextWriter (output);
158                         string oldExePath = request.CurrentExecutionFilePath;
159                         bool oldIsInclude = context.IsProcessingInclude;
160                         try {
161                                 context.PushHandler (handler);
162                                 if (is_static) // Not sure if this should apply to Page too
163                                         request.SetFilePath (exePath);
164
165                                 request.SetCurrentExePath (exePath);
166                                 context.IsProcessingInclude = isInclude;
167                                 
168                                 if (!(handler is IHttpAsyncHandler)) {
169                                         handler.ProcessRequest (context);
170                                 } else {
171                                         IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler;
172                                         IAsyncResult ar = asyncHandler.BeginProcessRequest (context, null, null);
173                                         WaitHandle asyncWaitHandle = ar != null ? ar.AsyncWaitHandle : null;
174                                         if (asyncWaitHandle != null)
175                                                 asyncWaitHandle.WaitOne ();
176                                         asyncHandler.EndProcessRequest (ar);
177                                 }
178                         } finally {
179                                 if (oldQuery != request.QueryStringRaw) {
180                                         if (oldQuery != null && oldQuery.Length > 0) {
181                                                 oldQuery = oldQuery.Substring (1); // Ignore initial '?'
182                                                 request.QueryStringRaw = oldQuery; // which is added here.
183                                         } else
184                                                 request.QueryStringRaw = String.Empty;
185                                 }
186                                 
187                                 response.SetTextWriter (previous);
188                                 if (!preserveForm)
189                                         request.SetForm (oldForm);
190
191                                 context.PopHandler ();
192                                 request.SetCurrentExePath (oldExePath);
193                                 context.IsProcessingInclude = oldIsInclude;
194                         }
195                 }
196
197                 public Exception GetLastError ()
198                 {
199                         if (context == null)
200                                 return HttpContext.Current.Error;
201                         return context.Error;
202                 }
203
204                 public string HtmlDecode (string s)
205                 {
206                         return HttpUtility.HtmlDecode (s);
207                 }
208
209                 public void HtmlDecode (string s, TextWriter output)
210                 {
211                         HttpUtility.HtmlDecode (s, output);
212                 }
213
214                 public string HtmlEncode (string s)
215                 {
216                         return HttpUtility.HtmlEncode (s);
217                 }
218
219                 public void HtmlEncode (string s, TextWriter output)
220                 {
221                         HttpUtility.HtmlEncode (s, output);
222                 }
223
224                 public string MapPath (string path)
225                 {
226                         return context.Request.MapPath (path);
227                 }
228
229                 
230                 public void TransferRequest (string path)
231                 {
232                         TransferRequest (path, false, null, null);
233                 }
234                 
235                 public void TransferRequest (string path, bool preserveForm)
236                 {
237                         TransferRequest (path, preserveForm, null, null);
238                 }
239
240                 [MonoTODO ("Always throws PlatformNotSupportedException.")]
241                 public void TransferRequest (string path, bool preserveForm, string method, NameValueCollection headers)
242                 {
243                         throw new PlatformNotSupportedException ();
244                 }
245                 
246                 public void Transfer (string path)
247                 {
248                         Transfer (path, true);
249                 }
250
251                 public void Transfer (string path, bool preserveForm) {
252                         Execute (path, null, preserveForm, true);
253                         context.Response.End ();
254                 }
255
256                 public void Transfer (IHttpHandler handler, bool preserveForm)
257                 {
258                         if (handler == null)
259                                 throw new ArgumentNullException ("handler");
260
261                         // TODO: see the MS doc and search for "enableViewStateMac": this method is not
262                         // allowed for pages when preserveForm is true and the page IsCallback property
263                         // is true.
264                         Execute (handler, null, preserveForm, context.Request.CurrentExecutionFilePath, null, true, true);
265                         context.Response.End ();
266                 }
267
268                 public void Execute (IHttpHandler handler, TextWriter writer, bool preserveForm)
269                 {
270                         if (handler == null)
271                                 throw new ArgumentNullException ("handler");
272
273                         Execute (handler, writer, preserveForm, context.Request.CurrentExecutionFilePath, null, false, true);
274                 }
275
276                 public static byte[] UrlTokenDecode (string input)
277                 {
278                         if (input == null)
279                                 throw new ArgumentNullException ("input");
280                         if (input.Length < 1)
281                                 return new byte[0];
282                         byte[] bytes = Encoding.ASCII.GetBytes (input);
283                         int inputLength = input.Length - 1;
284                         int equalsCount = (int)(((char)bytes[inputLength]) - 0x30);
285                         char[] ret = new char[inputLength + equalsCount];
286                         int i = 0;
287                         for (; i < inputLength; i++) {
288                                 switch ((char)bytes[i]) {
289                                         case '-':
290                                                 ret[i] = '+';
291                                                 break;
292
293                                         case '_':
294                                                 ret[i] = '/';
295                                                 break;
296
297                                         default:
298                                                 ret[i] = (char)bytes[i];
299                                                 break;
300                                 }
301                         }
302                         while (equalsCount > 0) {
303                                 ret[i++] = '=';
304                                 equalsCount--;
305                         }
306                         
307                         return Convert.FromBase64CharArray (ret, 0, ret.Length);
308                 }
309
310                 public static string UrlTokenEncode (byte[] input)
311                 {
312                         if (input == null)
313                                 throw new ArgumentNullException ("input");
314                         if (input.Length < 1)
315                                 return String.Empty;
316                         string base64 = Convert.ToBase64String (input);
317                         int retlen;
318                         if (base64 == null || (retlen = base64.Length) == 0)
319                                 return String.Empty;
320
321                         // MS.NET implementation seems to process the base64
322                         // string before returning. It replaces the chars:
323                         //
324                         //  + with -
325                         //  / with _
326                         //
327                         // Then removes trailing ==, which may appear in the
328                         // base64 string, and replaces them with a single digit
329                         // that's the count of removed '=' characters (0 if none
330                         // were removed)
331                         int equalsCount = 0x30;
332                         while (retlen > 0 && base64[retlen - 1] == '=') {
333                                 equalsCount++;
334                                 retlen--;
335                         }
336                         char[] chars = new char[retlen + 1];
337                         chars[retlen] = (char)equalsCount;
338                         for (int i = 0; i < retlen; i++) {
339                                 switch (base64[i]) {
340                                         case '+':
341                                                 chars[i] = '-';
342                                                 break;
343
344                                         case '/':
345                                                 chars[i] = '_';
346                                                 break;
347                                         
348                                         default:
349                                                 chars[i] = base64[i];
350                                                 break;
351                                 }
352                         }
353                         return new string (chars);
354                 }
355
356                 public string UrlDecode (string s)
357                 {
358                         HttpRequest request = context.Request;
359                         if(request != null)
360                                 return HttpUtility.UrlDecode (s, request.ContentEncoding);
361                         else
362                                 return HttpUtility.UrlDecode (s);
363                 }
364
365                 public void UrlDecode (string s, TextWriter output)
366                 {
367                         if (s != null)
368                                 output.Write (UrlDecode (s));
369                 }
370
371                 public string UrlEncode (string s)
372                 {
373                         HttpResponse response = context.Response;
374                         if (response != null)
375                                 return HttpUtility.UrlEncode (s, response.ContentEncoding);
376                         else
377                                 return HttpUtility.UrlEncode (s);
378                 }
379
380                 public void UrlEncode (string s, TextWriter output)
381                 {
382                         if (s != null)
383                                 output.Write (UrlEncode (s));
384                 }
385
386                 public string UrlPathEncode (string s)
387                 {
388                         if (s == null)
389                                 return null;
390
391                         int idx = s.IndexOf ('?');
392                         string s2 = null;
393                         if (idx != -1) {
394                                 s2 = s.Substring (0, idx);
395                                 s2 = HttpUtility.UrlEncode (s2) + s.Substring (idx);
396                         } else {
397                                 s2 = HttpUtility.UrlEncode (s);
398                         }
399
400                         return s2;
401                 }
402
403                 public string MachineName {
404                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
405                         // Medium doesn't look heavy enough to replace this... reported as
406                         [SecurityPermission (SecurityAction.Assert, UnmanagedCode = true)]
407                         [EnvironmentPermission (SecurityAction.Assert, Read = "COMPUTERNAME")]
408                         get { return Environment.MachineName; }
409                 }
410
411                 public int ScriptTimeout {
412                         get { return (int) context.ConfigTimeout.TotalSeconds; }
413                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
414                         set { context.ConfigTimeout = TimeSpan.FromSeconds (value); }
415                 }
416         }
417 }