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