2009-02-28 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpException.cs
1 // 
2 // System.Web.HttpException
3 //
4 // Authors:
5 //      Patrik Torstensson (Patrik.Torstensson@labs2.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (c) 2002 Patrik Torstensson
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.IO;
33 using System.Runtime.Serialization;
34 using System.Runtime.InteropServices;
35 using System.Security.Permissions;
36 using System.Text;
37 using System.Web.Util;
38 using System.Web.Compilation;
39 using System.Collections.Specialized;
40
41 namespace System.Web
42 {
43         // CAS
44         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
46 #if NET_2_0
47         [Serializable]
48 #endif
49         public class HttpException : ExternalException
50         {
51                 const string DEFAULT_DESCRIPTION_TEXT = "Error processing request.";
52                 const string ERROR_404_DESCRIPTION = "The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.";
53                 
54                 int http_code = 500;
55                 string resource_name;
56                 string description;
57                 
58                 const string errorStyleFonts = "\"Verdana\",\"DejaVu Sans\",sans-serif";
59                 
60                 public HttpException ()
61                 {
62                 }
63
64                 public HttpException (string message)
65                         : base (message)
66                 {
67                 }
68
69                 public HttpException (string message, Exception innerException)
70                         : base (message, innerException)
71                 {
72                 }
73
74                 public HttpException (int httpCode, string message) : base (message)
75                 {
76                         http_code = httpCode;
77                 }
78
79                 internal HttpException (int httpCode, string message, string resourceName) : this (httpCode, message)
80                 {
81                         resource_name = resourceName;
82                 }
83
84                 internal HttpException (int httpCode, string message, string resourceName, string description) : this (httpCode, message, resourceName)
85                 {
86                         this.description = description;
87                 }
88                 
89 #if NET_2_0
90                 protected HttpException (SerializationInfo info, StreamingContext context)
91                         : base (info, context)
92                 {
93                         http_code = info.GetInt32 ("_httpCode");
94                 }
95
96                 [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
97                 public override void GetObjectData (SerializationInfo info, StreamingContext context)
98                 {
99                         base.GetObjectData (info, context);
100                         info.AddValue ("_httpCode", http_code);
101                 }
102 #endif
103
104                 public HttpException (int httpCode, string message, int hr) 
105                         : base (message, hr)
106                 {
107                         http_code = httpCode;
108                 }
109
110                 public HttpException (string message, int hr)
111                         : base (message, hr)
112                 {
113                 }
114         
115                 public HttpException (int httpCode, string message, Exception innerException)
116                         : base (message, innerException)
117                 {
118                         http_code = httpCode;
119                 }
120
121                 internal HttpException (int httpCode, string message, Exception innerException, string resourceName)
122                         : this (httpCode, message, innerException)
123                 {
124                         resource_name = resourceName;
125                 }
126                 
127                 public string GetHtmlErrorMessage ()
128                 {
129                         try {
130                                 HttpContext ctx = HttpContext.Current;
131                                 if (ctx != null && ctx.IsCustomErrorEnabled)
132                                         return GetCustomErrorDefaultMessage ();
133                                 
134                                 if (!(this.InnerException is HtmlizedException))
135                                         return GetDefaultErrorMessage ();
136                                 
137                                 return GetHtmlizedErrorMessage ();
138                         } catch (Exception ex) {
139                                 Console.WriteLine (ex);
140                                 
141                                 // we need the try/catch block in case the
142                                 // problem was with MapPath, which will cause
143                                 // IsCustomErrorEnabled to throw an exception
144                                 return GetCustomErrorDefaultMessage ();
145                         }
146                 }
147
148                 internal virtual string Description {
149                         get {
150                                 if (description != null)
151                                         return description;
152
153                                 return DEFAULT_DESCRIPTION_TEXT;
154                         }
155                         
156                         set {
157                                 if (value != null && value.Length > 0)
158                                         description = value;
159                                 else
160                                         description = DEFAULT_DESCRIPTION_TEXT;
161                         }
162                 }
163
164                 void WriteFileTop (StringBuilder builder, string title)
165                 {
166 #if TARGET_J2EE
167                         builder.AppendFormat ("<html><head><title>{0}</title><style type=\"text/css\">", title);
168                         builder.AppendFormat (
169                                 @"body {{font-family:{0};font-weight:normal;font-size: 9pt;color:black;background-color: white}}
170 p {{font-family:{0};font-weight:normal;color:black;margin-top: -5px}}
171 b {{font-family:{0};font-weight:bold;color:black;margin-top: -5px}}
172 h1 {{ font-family:{0};font-weight:normal;font-size:18pt;color:red }}
173 h2 {{ font-family:{0};font-weight:normal;font-size:14pt;color:maroon }}
174 pre {{font-family:""Lucida Console"",""DejaVu Sans Mono"",monospace;font-size: 10pt}}
175 div.bodyText {{font-family: {0}}}
176 table.sampleCode {{width: 100%; background-color: #ffffcc; }}
177 .errorText {{color: red; font-weight: bold}}
178 .marker {{font-weight: bold; color: black;text-decoration: none;}}
179 .version {{color: gray;}}
180 .error {{margin-bottom: 10px;}}
181 .expandable {{ text-decoration:underline; font-weight:bold; color:navy; cursor:pointer; }}", errorStyleFonts);
182 #else
183                         builder.Append ("<?xml version=\"1.0\" ?>\n");
184                         builder.Append ("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
185                         builder.AppendFormat ("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><head><title>{0}</title><style type=\"text/css\">", title);
186                         builder.AppendFormat (
187                                 @"body {{font-family:{0};font-weight:normal;font-size: .7em;color:black;background-color: white}}
188 p {{font-family:{0};font-weight:normal;color:black;margin-top: -5px}}
189 b {{font-family:{0};font-weight:bold;color:black;margin-top: -5px}}
190 h1 {{ font-family:{0};font-weight:normal;font-size:18pt;color:red }}
191 h2 {{ font-family:{0};font-weight:normal;font-size:14pt;color:maroon }}
192 pre,code {{font-family:""Lucida Console"",""DejaVu Sans Mono"",monospace;font-size: 0.9em,white-space: pre-line}}
193 div.bodyText {{font-family: {0}}}
194 table.sampleCode {{width: 100%; background-color: #ffffcc; }}
195 .errorText {{color: red; font-weight: bold}}
196 .marker {{font-weight: bold; color: black;text-decoration: none;}}
197 .version {{color: gray;}}
198 .error {{margin-bottom: 10px;}}
199 .expandable {{ text-decoration:underline; font-weight:bold; color:navy; cursor:pointer; }}", errorStyleFonts);
200 #endif
201
202                         builder.AppendFormat (
203                                 "</style></head><body><h1>Server Error in '{0}' Application</h1><hr style=\"color: silver\"/>",
204                                 HtmlEncode (HttpRuntime.AppDomainAppVirtualPath));
205                 }
206                 
207                 void WriteFileBottom (StringBuilder builder, bool showTrace)
208                 {
209                         builder.Append ("<hr style=\"color: silver\"/>");
210                         builder.AppendFormat ("<strong>Version information: </strong> Mono Version: {0}; ASP.NET Version: {0}</body></html>\r\n<!--", Environment.Version);
211                         if (!showTrace)
212                                 return;
213                         
214                         string trace, message;
215                         bool haveTrace;
216                         Exception ex = this;
217                         
218                         while (ex != null) {
219                                 trace = ex.StackTrace;
220                                 message = ex.Message;
221                                 haveTrace = (trace != null && trace.Length > 0);
222                                 
223                                 if (!haveTrace && (message == null || message.Length == 0)) {
224                                         ex = ex.InnerException;
225                                         continue;
226                                 }
227
228                                 builder.Append ("\r\n[" + ex.GetType () + "]: " + HtmlEncode (message) + "\r\n");
229                                 if (haveTrace)
230                                         builder.Append (ex.StackTrace);
231                                 
232                                 ex = ex.InnerException;
233                         }
234                         
235                         builder.Append ("\r\n-->");
236                 }
237
238                 string GetCustomErrorDefaultMessage ()
239                 {
240                         StringBuilder builder = new StringBuilder ();
241                         WriteFileTop (builder, "Runtime Error");
242 #if TARGET_J2EE //on portal we cannot know if we run locally
243                         if (!HttpContext.Current.IsServletRequest)
244                                 builder.Append (
245 @"<p><strong>Description:</strong> An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed (for security reasons).</p>
246 <p><strong>Details:</strong> To enable the details of this specific error message to be viewable, please create a &lt;customErrors&gt; tag within a &quot;web.config&quot; configuration file located in the root directory of the current web application. This &lt;customErrors&gt; tag should then have its &quot;mode&quot; attribute set to &quot;Off&quot;.</p>
247 <table class=""sampleCode""><tr><td><pre>
248
249 &lt;!-- Web.Config Configuration File --&gt;
250
251 &lt;configuration&gt;
252     &lt;system.web&gt;
253
254         &lt;customErrors mode=&quot;Off&quot;/&gt;
255     &lt;/system.web&gt;
256 &lt;/configuration&gt;</pre>
257     </td></tr></table><br/>
258 ");
259                         else
260 #endif
261                         builder.Append (@"<p><strong>Description:</strong> An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons)." + (
262                                 " It could, however, be viewed by browsers running on the local server machine.") 
263                                 + @"</p>
264 <p><strong>Details:</strong> To enable the details of this specific error message to be viewable on remote machines, please create a &lt;customErrors&gt; tag within a &quot;web.config&quot; configuration file located in the root directory of the current web application. This &lt;customErrors&gt; tag should then have its &quot;mode&quot; attribute set to &quot;Off&quot;.</p>
265 <table class=""sampleCode""><tr><td><pre>
266
267 &lt;!-- Web.Config Configuration File --&gt;
268
269 &lt;configuration&gt;
270     &lt;system.web&gt;
271
272         &lt;customErrors mode=&quot;Off&quot;/&gt;
273     &lt;/system.web&gt;
274 &lt;/configuration&gt;</pre>
275     </td></tr></table><br/>
276 <p><strong>Notes:</strong> The current error page you are seeing can be replaced by a custom error page by modifying the &quot;defaultRedirect&quot; attribute of the application's &lt;customErrors&gt; configuration tag to point to a custom error page URL.</p>
277 <table class=""sampleCode""><tr><td><pre>
278 &lt;!-- Web.Config Configuration File --&gt;
279
280 &lt;configuration&gt;
281     &lt;system.web&gt;
282         &lt;customErrors mode=&quot;RemoteOnly&quot; defaultRedirect=&quot;mycustompage.htm&quot;/&gt;
283
284     &lt;/system.web&gt;
285 &lt;/configuration&gt;</pre></td></tr></table>");
286                         WriteFileBottom (builder, false);
287                         return builder.ToString ();
288                 }
289                 
290                 string GetDefaultErrorMessage ()
291                 {
292                         Exception ex, baseEx;
293                         ex = baseEx = GetBaseException ();
294                         if (ex == null)
295                                 ex = this;
296
297                         StringBuilder builder = new StringBuilder ();
298                         WriteFileTop (builder, String.Format ("Error{0}", http_code != 0 ? " " + http_code : String.Empty));
299                         builder.Append ("<h2><em>");
300                         if (http_code == 404)
301                                 builder.Append ("The resource cannot be found.");
302                         else
303                                 builder.Append (HtmlEncode (ex.Message));
304                         builder.Append ("</em></h2>\r\n<p><strong>Description: </strong>");
305                         
306                         if (http_code != 0)
307                                 builder.AppendFormat ("HTTP {0}. ", http_code);
308                         builder.Append (http_code == 404 ? ERROR_404_DESCRIPTION : HtmlEncode (Description));
309                         builder.Append ("</p>\r\n");
310
311                         if (resource_name != null && resource_name.Length > 0)
312                                 builder.AppendFormat ("<p><strong>Resource URL: </strong>{0}</p>\r\n", resource_name);
313                         
314                         if (baseEx != null && http_code != 404) {
315                                 builder.Append ("<p><strong>Stack Trace: </strong></p>");
316                                 builder.Append ("<table summary=\"Stack Trace\" class=\"sampleCode\">\r\n<tr><td>");
317                                 WriteTextAsCode (builder, baseEx.ToString ());
318                                 builder.Append ("</td></tr>\r\n</table>\r\n");
319                         }
320                         WriteFileBottom (builder, true);
321                         
322                         return builder.ToString ();
323                 }
324
325                 static string HtmlEncode (string s)
326                 {
327                         if (s == null)
328                                 return s;
329
330                         string res = HttpUtility.HtmlEncode (s);
331                         return res.Replace ("\r\n", "<br />");
332                 }
333
334                 string GetHtmlizedErrorMessage ()
335                 {
336                         StringBuilder builder = new StringBuilder ();
337                         HtmlizedException exc = (HtmlizedException) this.InnerException;
338 #if TARGET_J2EE
339                         bool isParseException = false;
340                         bool isCompileException = false;
341 #else
342                         bool isParseException = exc is ParseException;
343                         bool isCompileException = (!isParseException && exc is CompilationException);
344 #endif
345                         
346                         WriteFileTop (builder, exc.Title);
347                         builder.AppendFormat ("<h2><em>{0}</em></h2>\r\n", exc.Title);
348                         builder.AppendFormat ("<p><strong>Description: </strong>{0}\r\n</p>\r\n", HtmlEncode (exc.Description));
349                         string errorMessage = HtmlEncode (exc.ErrorMessage);
350                         
351                         builder.Append ("<p><strong>");
352                         if (isParseException)
353                                 builder.Append ("Parser ");
354                         else if (isCompileException)
355                                 builder.Append ("Compiler ");
356                         
357                         builder.Append ("Error Message: </strong>");
358 #if NET_2_0
359                         builder.AppendFormat ("<code>{0}</code></p>", errorMessage);
360 #else
361                         builder.AppendFormat ("<blockquote><pre>{0}</pre></blockquote></p>", errorMessage);
362 #endif
363
364                         StringBuilder longCodeVersion = null;
365                         
366                         if (exc.FileText != null) {
367                                 if (isParseException || isCompileException) {
368                                         builder.Append ("<p><strong>Source Error: </strong></p>\r\n");
369                                         builder.Append ("<table summary=\"Source error\" class=\"sampleCode\">\r\n<tr><td>");
370
371                                         if (isCompileException)
372                                                 longCodeVersion = new StringBuilder ();
373                                         WriteSource (builder, longCodeVersion, exc);
374                                         builder.Append ("</pre></code></td></tr>\r\n</table>\r\n");
375                                 } else {
376                                         builder.Append ("<table summary=\"Source file\" class=\"sampleCode\">\r\n<tr><td>");
377                                         WriteSource (builder, null, exc);
378                                         builder.Append ("</pre></code></td></tr>\r\n</table>\r\n");
379                                 }
380
381                                 builder.Append ("<br/><p><strong>Source File: </strong>");
382                                 if (exc.SourceFile != exc.FileName)
383                                         builder.Append (exc.SourceFile);
384                                 else
385                                         builder.Append (exc.FileName);
386
387                                 if ((isParseException || isCompileException) && exc.ErrorLines.Length > 0) {
388                                         builder.Append ("&nbsp;&nbsp;<strong>Line: </strong>");
389                                         builder.Append (exc.ErrorLines [0]);
390                                 }
391                                 builder.Append ("</p>");
392                         } else if (exc.FileName != null)
393                                 builder.AppendFormat ("{0}</p>", HtmlEncode (exc.FileName));
394
395                         bool needToggleJS = false;
396                         
397 #if !TARGET_J2EE
398                         if (isCompileException) {
399                                 CompilationException cex = exc as CompilationException;
400                                 StringCollection output = cex.CompilerOutput;
401
402                                 if (output != null && output.Count > 0) {
403                                         needToggleJS = true;
404                                         StringBuilder sb = new StringBuilder ();
405                                         foreach (string s in output)
406                                                 sb.Append (s + "\r\n");
407                                         WriteExpandableBlock (builder, "compilerOutput", "Show Detailed Compiler Output", sb.ToString ());
408                                 }
409                         }
410 #endif
411                         
412                         if (longCodeVersion != null && longCodeVersion.Length > 0) {
413                                 WriteExpandableBlock (builder, "fullCode", "Show Complete Compilation Source", longCodeVersion.ToString ());
414                                 needToggleJS = true;
415                         }
416
417                         if (needToggleJS)
418                                 builder.Append ("<script type=\"text/javascript\">\r\n" +
419                                                 "function ToggleVisible (id)\r\n" +
420                                                 "{\r\n" +
421                                                 "\tvar e = document.getElementById (id);\r\n" +
422                                                 "\tif (e.style.display == 'none')\r\n" +
423                                                 "\t{\r\n" +
424                                                 "\t\te.style.display = '';\r\n" +
425                                                 "\t} else {\r\n" +
426                                                 "\t\te.style.display = 'none';\r\n" +
427                                                 "\t}\r\n" +
428                                                 "}\r\n" +
429                                                 "</script>\r\n");
430                         
431                         WriteFileBottom (builder, true);
432                         
433                         return builder.ToString ();
434                 }
435
436                 static void WriteExpandableBlock (StringBuilder builder, string id, string title, string contents)
437                 {
438                         builder.AppendFormat ("<br><div class=\"expandable\" onclick=\"ToggleVisible ('{1}')\">{0}:</div><br/>" +
439                                               "<div id=\"{1}\" style=\"display: none\"><table summary=\"Details\" class=\"sampleCode\"><tr><td>" +
440                                               "<code><pre>\r\n", title, id);
441                         builder.Append (contents);
442                         builder.Append ("</pre></code></td></tr></table></div>");
443                 }
444                 
445                 static void WriteTextAsCode (StringBuilder builder, string text)
446                 {
447                         builder.AppendFormat ("<pre>{0}</pre>", HtmlEncode (text));
448                 }
449
450 #if TARGET_J2EE
451                 static void WriteSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
452                 {
453                         builder.Append ("<code><pre>");
454                         WritePageSource (builder, e);
455                         builder.Append ("</code></pre>\r\n");
456                 }
457
458 #else
459                 static void WriteSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
460                 {
461                         builder.Append ("<code><pre>");
462                         if (e is CompilationException)
463                                 WriteCompilationSource (builder, longVersion, e);
464                         else
465                                 WritePageSource (builder, e);
466
467                         builder.Append ("<code></pre>\r\n");
468                 }
469 #endif
470                 
471                 static void WriteCompilationSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
472                 {
473                         int [] a = e.ErrorLines;
474                         string s;
475                         int line = 0;
476                         int index = 0;
477                         int errline = 0;
478
479                         if (a != null && a.Length > 0)
480                                 errline = a [0];
481
482                         int begin = errline - 2;
483                         int end = errline + 2;
484
485                         if (begin < 0)
486                                 begin = 0;
487
488                         string tmp;                     
489                         using (TextReader reader = new StringReader (e.FileText)) {
490                                 while ((s = reader.ReadLine ()) != null) {
491                                         line++;
492                                         if (line < begin || line > end) {
493                                                 if (longVersion != null)
494                                                         longVersion.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
495                                                 continue;
496                                         }
497                                 
498                                         if (errline == line) {
499                                                 if (longVersion != null)
500                                                         longVersion.Append ("<span style=\"color: red\">");
501                                                 builder.Append ("<span style=\"color: red\">");
502                                         }
503                                         
504                                         tmp = String.Format ("Line {0}: {1}\r\n", line, HtmlEncode (s));
505                                         builder.Append (tmp);
506                                         if (longVersion != null)
507                                                 longVersion.Append (tmp);
508                                         
509                                         if (line == errline) {
510                                                 builder.Append ("</span>");
511                                                 if (longVersion != null)
512                                                         longVersion.Append ("</span>");
513                                                 errline = (++index < a.Length) ? a [index] : 0;
514                                         }
515                                 }
516                         }                       
517                 }
518
519                 static void WritePageSource (StringBuilder builder, HtmlizedException e)
520                 {
521                         string s;
522                         int line = 0;
523                         int beginerror = e.ErrorLines [0];
524                         int enderror = e.ErrorLines [1];
525                         int begin = beginerror - 2;
526                         int end = enderror + 2;
527                         if (begin <= 0)
528                                 begin = 1;
529                         
530                         TextReader reader = new StringReader (e.FileText);
531                         while ((s = reader.ReadLine ()) != null) {
532                                 line++;
533                                 if (line < begin)
534                                         continue;
535
536                                 if (line > end)
537                                         break;
538
539                                 if (beginerror == line)
540                                         builder.Append ("<span style=\"color: red\">");
541
542                                 builder.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
543
544                                 if (enderror <= line) {
545                                         builder.Append ("</span>");
546                                         enderror = end + 1; // one shot
547                                 }
548                         }
549                 }
550                 
551                 public int GetHttpCode ()
552                 {
553                         return http_code;
554                 }
555
556                 public static HttpException CreateFromLastError (string message)
557                 {
558                         WebTrace.WriteLine ("CreateFromLastError");
559                         return new HttpException (message, 0);
560                 }
561         }
562 }
563