2010-03-12 Jb Evain <jbevain@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-2009 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         [Serializable]
47         public class HttpException : ExternalException
48         {
49                 const string DEFAULT_DESCRIPTION_TEXT = "Error processing request.";
50                 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.";
51                 
52                 int http_code = 500;
53                 string resource_name;
54                 string description;
55                 
56                 const string errorStyleFonts = "\"Verdana\",\"DejaVu Sans\",sans-serif";
57                 
58                 public HttpException ()
59                 {
60                 }
61
62                 public HttpException (string message)
63                         : base (message)
64                 {
65                 }
66
67                 public HttpException (string message, Exception innerException)
68                         : base (message, innerException)
69                 {
70                 }
71
72                 public HttpException (int httpCode, string message) : base (message)
73                 {
74                         http_code = httpCode;
75                 }
76
77                 internal HttpException (int httpCode, string message, string resourceName) : this (httpCode, message)
78                 {
79                         resource_name = resourceName;
80                 }
81
82                 internal HttpException (int httpCode, string message, string resourceName, string description) : this (httpCode, message, resourceName)
83                 {
84                         this.description = description;
85                 }
86                 
87                 protected HttpException (SerializationInfo info, StreamingContext context)
88                         : base (info, context)
89                 {
90                         http_code = info.GetInt32 ("_httpCode");
91                 }
92
93                 [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
94                 public override void GetObjectData (SerializationInfo info, StreamingContext context)
95                 {
96                         base.GetObjectData (info, context);
97                         info.AddValue ("_httpCode", http_code);
98                 }
99
100                 public HttpException (int httpCode, string message, int hr) 
101                         : base (message, hr)
102                 {
103                         http_code = httpCode;
104                 }
105
106                 public HttpException (string message, int hr)
107                         : base (message, hr)
108                 {
109                 }
110         
111                 public HttpException (int httpCode, string message, Exception innerException)
112                         : base (message, innerException)
113                 {
114                         http_code = httpCode;
115                 }
116
117                 internal HttpException (int httpCode, string message, Exception innerException, string resourceName)
118                         : this (httpCode, message, innerException)
119                 {
120                         resource_name = resourceName;
121                 }
122                 
123                 public string GetHtmlErrorMessage ()
124                 {
125                         try {
126                                 HttpContext ctx = HttpContext.Current;
127                                 if (ctx != null && ctx.IsCustomErrorEnabled) {
128                                         if (http_code != 404 && http_code != 403)
129                                                 return GetCustomErrorDefaultMessage ();
130                                         else
131                                                 return GetDefaultErrorMessage (false);
132                                 }
133                                 
134                                 if (!(this.InnerException is HtmlizedException))
135                                         return GetDefaultErrorMessage (true);
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                         if (showTrace) {
210                                 builder.Append ("<hr style=\"color: silver\"/>");
211                                 builder.AppendFormat ("<strong>Version information: </strong> Mono Runtime Version: <tt>{0}</tt>; ASP.NET Version: <tt>{1}</tt></body></html>\r\n",
212                                                       RuntimeHelpers.MonoVersion, Environment.Version);
213                         
214                                 string trace, message;
215                                 bool haveTrace;
216                                 Exception ex = this;
217
218                                 builder.Append ("\r\n<!--");
219                                 while (ex != null) {
220                                         trace = ex.StackTrace;
221                                         message = ex.Message;
222                                         haveTrace = (trace != null && trace.Length > 0);
223                                 
224                                         if (!haveTrace && (message == null || message.Length == 0)) {
225                                                 ex = ex.InnerException;
226                                                 continue;
227                                         }
228
229                                         builder.Append ("\r\n[" + ex.GetType () + "]: " + HtmlEncode (message) + "\r\n");
230                                         if (haveTrace)
231                                                 builder.Append (ex.StackTrace);
232                                 
233                                         ex = ex.InnerException;
234                                 }
235                         
236                                 builder.Append ("\r\n-->");
237                         } else
238                                 builder.Append ("</body></html>\r\n");
239                 }
240
241                 string GetCustomErrorDefaultMessage ()
242                 {
243                         StringBuilder builder = new StringBuilder ();
244                         WriteFileTop (builder, "Runtime Error");
245 #if TARGET_J2EE //on portal we cannot know if we run locally
246                         if (!HttpContext.Current.IsServletRequest)
247                                 builder.Append (
248 @"<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>
249 <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>
250 <table class=""sampleCode""><tr><td><pre>
251
252 &lt;!-- Web.Config Configuration File --&gt;
253
254 &lt;configuration&gt;
255     &lt;system.web&gt;
256
257         &lt;customErrors mode=&quot;Off&quot;/&gt;
258     &lt;/system.web&gt;
259 &lt;/configuration&gt;</pre>
260     </td></tr></table><br/>
261 ");
262                         else
263 #endif
264                         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)." + (
265                                 " It could, however, be viewed by browsers running on the local server machine.") 
266                                 + @"</p>
267 <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>
268 <table class=""sampleCode""><tr><td><pre>
269
270 &lt;!-- Web.Config Configuration File --&gt;
271
272 &lt;configuration&gt;
273     &lt;system.web&gt;
274
275         &lt;customErrors mode=&quot;Off&quot;/&gt;
276     &lt;/system.web&gt;
277 &lt;/configuration&gt;</pre>
278     </td></tr></table><br/>
279 <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>
280 <table class=""sampleCode""><tr><td><pre>
281 &lt;!-- Web.Config Configuration File --&gt;
282
283 &lt;configuration&gt;
284     &lt;system.web&gt;
285         &lt;customErrors mode=&quot;RemoteOnly&quot; defaultRedirect=&quot;mycustompage.htm&quot;/&gt;
286
287     &lt;/system.web&gt;
288 &lt;/configuration&gt;</pre></td></tr></table>");
289                         WriteFileBottom (builder, false);
290                         return builder.ToString ();
291                 }
292                 
293                 string GetDefaultErrorMessage (bool showTrace)
294                 {
295                         Exception ex, baseEx;
296                         ex = baseEx = GetBaseException ();
297                         if (ex == null)
298                                 ex = this;
299
300                         StringBuilder builder = new StringBuilder ();
301                         WriteFileTop (builder, String.Format ("Error{0}", http_code != 0 ? " " + http_code : String.Empty));
302                         builder.Append ("<h2><em>");
303                         if (http_code == 404)
304                                 builder.Append ("The resource cannot be found.");
305                         else
306                                 builder.Append (HtmlEncode (ex.Message));
307                         builder.Append ("</em></h2>\r\n<p><strong>Description: </strong>");
308                         
309                         if (http_code != 0)
310                                 builder.AppendFormat ("HTTP {0}. ", http_code);
311                         builder.Append (http_code == 404 ? ERROR_404_DESCRIPTION : HtmlEncode (Description));
312                         builder.Append ("</p>\r\n");
313
314                         if (resource_name != null && resource_name.Length > 0)
315                                 builder.AppendFormat ("<p><strong>Requested URL: </strong>{0}</p>\r\n", resource_name);
316                         
317                         if (showTrace && baseEx != null && http_code != 404 && http_code != 403) {
318                                 builder.Append ("<p><strong>Stack Trace: </strong></p>");
319                                 builder.Append ("<table summary=\"Stack Trace\" class=\"sampleCode\">\r\n<tr><td>");
320                                 WriteTextAsCode (builder, baseEx.ToString ());
321                                 builder.Append ("</td></tr>\r\n</table>\r\n");
322                         }
323                         WriteFileBottom (builder, showTrace);
324                         
325                         return builder.ToString ();
326                 }
327
328                 static string HtmlEncode (string s)
329                 {
330                         if (s == null)
331                                 return s;
332
333                         string res = HttpUtility.HtmlEncode (s);
334                         return res.Replace ("\r\n", "<br />");
335                 }
336
337                 string FormatSourceFile (string filename)
338                 {
339                         if (filename == null || filename.Length == 0)
340                                 return String.Empty;
341
342                         if (filename.StartsWith ("@@"))
343                                 return "[internal] <!-- " + filename + " -->";
344
345                         return filename;
346                 }
347                 
348                 string GetHtmlizedErrorMessage ()
349                 {
350                         StringBuilder builder = new StringBuilder ();
351                         HtmlizedException exc = (HtmlizedException) this.InnerException;
352 #if TARGET_J2EE
353                         bool isParseException = false;
354                         bool isCompileException = false;
355 #else
356                         bool isParseException = exc is ParseException;
357                         bool isCompileException = (!isParseException && exc is CompilationException);
358 #endif
359                         
360                         WriteFileTop (builder, exc.Title);
361                         builder.AppendFormat ("<h2><em>{0}</em></h2>\r\n", exc.Title);
362                         builder.AppendFormat ("<p><strong>Description: </strong>{0}\r\n</p>\r\n", HtmlEncode (exc.Description));
363                         string errorMessage = HtmlEncode (exc.ErrorMessage);
364                         
365                         builder.Append ("<p><strong>");
366                         if (isParseException)
367                                 builder.Append ("Parser ");
368                         else if (isCompileException)
369                                 builder.Append ("Compiler ");
370                         
371                         builder.Append ("Error Message: </strong>");
372                         builder.AppendFormat ("<code>{0}</code></p>", errorMessage);
373
374                         StringBuilder longCodeVersion = null;
375                         
376                         if (exc.FileText != null) {
377                                 if (isParseException || isCompileException) {
378                                         builder.Append ("<p><strong>Source Error: </strong></p>\r\n");
379                                         builder.Append ("<table summary=\"Source error\" class=\"sampleCode\">\r\n<tr><td>");
380
381                                         if (isCompileException)
382                                                 longCodeVersion = new StringBuilder ();
383                                         WriteSource (builder, longCodeVersion, exc);
384                                         builder.Append ("</pre></code></td></tr>\r\n</table>\r\n");
385                                 } else {
386                                         builder.Append ("<table summary=\"Source file\" class=\"sampleCode\">\r\n<tr><td>");
387                                         WriteSource (builder, null, exc);
388                                         builder.Append ("</pre></code></td></tr>\r\n</table>\r\n");
389                                 }
390
391                                 builder.Append ("<br/><p><strong>Source File: </strong>");
392                                 if (exc.SourceFile != exc.FileName)
393                                         builder.Append (FormatSourceFile (exc.SourceFile));
394                                 else
395                                         builder.Append (FormatSourceFile (exc.FileName));
396
397                                 if (isParseException || isCompileException) {
398                                         int[] errorLines = exc.ErrorLines;
399                                         int numErrors = errorLines != null ? errorLines.Length : 0;
400                                         if (numErrors > 0) {
401                                                 builder.AppendFormat ("&nbsp;&nbsp;<strong>Line{0}: </strong>", numErrors > 1 ? "s" : String.Empty);
402                                                 for (int i = 0; i < numErrors; i++) {
403                                                         if (i > 0)
404                                                                 builder.Append (", ");
405                                                         builder.Append (exc.ErrorLines [i]);
406                                                 }
407                                         }
408                                 }
409                                 builder.Append ("</p>");
410                         } else if (exc.FileName != null)
411                                 builder.AppendFormat ("{0}</p>", HtmlEncode (exc.FileName));
412
413                         bool needToggleJS = false;
414                         
415 #if !TARGET_J2EE
416                         if (isCompileException) {
417                                 CompilationException cex = exc as CompilationException;
418                                 StringCollection output = cex.CompilerOutput;
419
420                                 if (output != null && output.Count > 0) {
421                                         needToggleJS = true;
422                                         StringBuilder sb = new StringBuilder ();
423                                         foreach (string s in output)
424                                                 sb.Append (s + "\r\n");
425                                         WriteExpandableBlock (builder, "compilerOutput", "Show Detailed Compiler Output", sb.ToString ());
426                                 }
427                         }
428 #endif
429                         
430                         if (longCodeVersion != null && longCodeVersion.Length > 0) {
431                                 WriteExpandableBlock (builder, "fullCode", "Show Complete Compilation Source", longCodeVersion.ToString ());
432                                 needToggleJS = true;
433                         }
434
435                         if (needToggleJS)
436                                 builder.Append ("<script type=\"text/javascript\">\r\n" +
437                                                 "function ToggleVisible (id)\r\n" +
438                                                 "{\r\n" +
439                                                 "\tvar e = document.getElementById (id);\r\n" +
440                                                 "\tif (e.style.display == 'none')\r\n" +
441                                                 "\t{\r\n" +
442                                                 "\t\te.style.display = '';\r\n" +
443                                                 "\t} else {\r\n" +
444                                                 "\t\te.style.display = 'none';\r\n" +
445                                                 "\t}\r\n" +
446                                                 "}\r\n" +
447                                                 "</script>\r\n");
448                         
449                         WriteFileBottom (builder, true);
450                         
451                         return builder.ToString ();
452                 }
453
454                 static void WriteExpandableBlock (StringBuilder builder, string id, string title, string contents)
455                 {
456                         builder.AppendFormat ("<br><div class=\"expandable\" onclick=\"ToggleVisible ('{1}')\">{0}:</div><br/>" +
457                                               "<div id=\"{1}\" style=\"display: none\"><table summary=\"Details\" class=\"sampleCode\"><tr><td>" +
458                                               "<code><pre>\r\n", title, id);
459                         builder.Append (contents);
460                         builder.Append ("</pre></code></td></tr></table></div>");
461                 }
462                 
463                 static void WriteTextAsCode (StringBuilder builder, string text)
464                 {
465                         builder.AppendFormat ("<pre>{0}</pre>", HtmlEncode (text));
466                 }
467
468 #if TARGET_J2EE
469                 static void WriteSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
470                 {
471                         builder.Append ("<code><pre>");
472                         WritePageSource (builder, e);
473                         builder.Append ("</code></pre>\r\n");
474                 }
475
476 #else
477                 static void WriteSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
478                 {
479                         builder.Append ("<code><pre>");
480                         if (e is CompilationException)
481                                 WriteCompilationSource (builder, longVersion, e);
482                         else
483                                 WritePageSource (builder, e);
484
485                         builder.Append ("<code></pre>\r\n");
486                 }
487 #endif
488                 
489                 static void WriteCompilationSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
490                 {
491                         int [] a = e.ErrorLines;
492                         string s;
493                         int line = 0;
494                         int index = 0;
495                         int errline = 0;
496
497                         if (a != null && a.Length > 0)
498                                 errline = a [0];
499
500                         int begin = errline - 2;
501                         int end = errline + 2;
502
503                         if (begin < 0)
504                                 begin = 0;
505
506                         string tmp;                     
507                         using (TextReader reader = new StringReader (e.FileText)) {
508                                 while ((s = reader.ReadLine ()) != null) {
509                                         line++;
510                                         if (line < begin || line > end) {
511                                                 if (longVersion != null)
512                                                         longVersion.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
513                                                 continue;
514                                         }
515                                 
516                                         if (errline == line) {
517                                                 if (longVersion != null)
518                                                         longVersion.Append ("<span style=\"color: red\">");
519                                                 builder.Append ("<span style=\"color: red\">");
520                                         }
521                                         
522                                         tmp = String.Format ("Line {0}: {1}\r\n", line, HtmlEncode (s));
523                                         builder.Append (tmp);
524                                         if (longVersion != null)
525                                                 longVersion.Append (tmp);
526                                         
527                                         if (line == errline) {
528                                                 builder.Append ("</span>");
529                                                 if (longVersion != null)
530                                                         longVersion.Append ("</span>");
531                                                 errline = (++index < a.Length) ? a [index] : 0;
532                                         }
533                                 }
534                         }                       
535                 }
536
537                 static void WritePageSource (StringBuilder builder, HtmlizedException e)
538                 {
539                         string s;
540                         int line = 0;
541                         int beginerror = e.ErrorLines [0];
542                         int enderror = e.ErrorLines [1];
543                         int begin = beginerror - 2;
544                         int end = enderror + 2;
545                         if (begin <= 0)
546                                 begin = 1;
547                         
548                         TextReader reader = new StringReader (e.FileText);
549                         while ((s = reader.ReadLine ()) != null) {
550                                 line++;
551                                 if (line < begin)
552                                         continue;
553
554                                 if (line > end)
555                                         break;
556
557                                 if (beginerror == line)
558                                         builder.Append ("<span style=\"color: red\">");
559
560                                 builder.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
561
562                                 if (enderror <= line) {
563                                         builder.Append ("</span>");
564                                         enderror = end + 1; // one shot
565                                 }
566                         }
567                 }
568                 
569                 public int GetHttpCode ()
570                 {
571                         return http_code;
572                 }
573
574                 public static HttpException CreateFromLastError (string message)
575                 {
576                         WebTrace.WriteLine ("CreateFromLastError");
577                         return new HttpException (message, 0);
578                 }
579         }
580 }
581