2007-05-16 Marek Habersack <mhabersack@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
40 namespace System.Web
41 {
42         // CAS
43         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 #if NET_2_0
46         [Serializable]
47 #endif
48         public class HttpException : ExternalException
49         {
50                 int http_code = 500;
51                 const string errorStyleFonts = "\"Verdana\",\"DejaVu Sans\",sans-serif";
52                 
53                 public HttpException ()
54                 {
55                 }
56
57                 public HttpException (string message)
58                         : base (message)
59                 {
60                 }
61
62                 public HttpException (string message, Exception innerException)
63                         : base (message, innerException)
64                 {
65                 }
66
67                 public HttpException (int httpCode, string message) : base (message)
68                 {
69                         http_code = httpCode;
70                 }
71
72 #if NET_2_0
73                 protected HttpException (SerializationInfo info, StreamingContext context)
74                         : base (info, context)
75                 {
76                         http_code = info.GetInt32 ("_httpCode");
77                 }
78
79                 [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
80                 public override void GetObjectData (SerializationInfo info, StreamingContext context)
81                 {
82                         base.GetObjectData (info, context);
83                         info.AddValue ("_httpCode", http_code);
84                 }
85 #endif
86
87                 public HttpException (int httpCode, string message, int hr) 
88                         : base (message, hr)
89                 {
90                         http_code = httpCode;
91                 }
92
93                 public HttpException (string message, int hr)
94                         : base (message, hr)
95                 {
96                 }
97         
98                 public HttpException (int httpCode, string message, Exception innerException)
99                         : base (message, innerException)
100                 {
101                         http_code = httpCode;
102                 }
103
104                 public string GetHtmlErrorMessage ()
105                 {
106                         try {
107                                 if (HttpContext.Current.IsCustomErrorEnabled)
108                                         return GetCustomErrorDefaultMessage ();
109                                 
110                                 if (!(this.InnerException is HtmlizedException))
111                                         return GetDefaultErrorMessage ();
112                                 
113                                 return GetHtmlizedErrorMessage ();
114                         } catch {
115                                 // we need the try/catch block in case the
116                                 // problem was with MapPath, which will cause
117                                 // IsCustomErrorEnabled to throw an exception
118                                 return GetCustomErrorDefaultMessage ();
119                         }
120                 }
121
122                 internal virtual string Description {
123                         get { return "Error processing request."; }
124                 }
125
126                 void WriteFileTop (StringBuilder builder, string title)
127                 {
128                         builder.Append ("<?xml version=\"1.0\" ?>\n");
129                         builder.Append ("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
130                         builder.AppendFormat ("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><head><title>{0}</title><style type=\"text/css\">", title);
131                         builder.AppendFormat (
132                                 @"body {{font-family:{0};font-weight:normal;font-size: .7em;color:black;background-color: white}}
133 p {{font-family:{0};font-weight:normal;color:black;margin-top: -5px}}
134 b {{font-family:{0};font-weight:bold;color:black;margin-top: -5px}}
135 h1 {{ font-family:{0};font-weight:normal;font-size:18pt;color:red }}
136 h2 {{ font-family:{0};font-weight:normal;font-size:14pt;color:maroon }}
137 pre {{font-family:""Lucida Console"",""DejaVu Sans Mono"",      monospace;font-size: 1.2em}}
138 div.bodyText {{font-family: {0}}}
139 table.sampleCode {{width: 100%; background-color: #ffffcc; }}
140 .errorText {{color: red; font-weight: bold}}
141 .marker {{font-weight: bold; color: black;text-decoration: none;}}
142 .version {{color: gray;}}
143 .error {{margin-bottom: 10px;}}
144 .expandable {{ text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }}", errorStyleFonts);
145
146                         builder.AppendFormat (
147                                 "</style></head><body><h1>Server Error in '{0}' Application</h1><hr style=\"color: silver\"/>",
148                                 HtmlEncode (HttpRuntime.AppDomainAppVirtualPath));
149                 }
150
151                 void WriteFileBottom (StringBuilder builder, string trace1, string trace2)
152                 {
153                         if (trace1 != null)
154                                 builder.AppendFormat ("<![CDATA[\r\n{0}\r\n]]>\r\n", HttpUtility.HtmlEncode (trace1));
155                         if (trace2 != null)
156                                 builder.AppendFormat ("<![CDATA[\r\n{0}\r\n]]>\r\n", HttpUtility.HtmlEncode (trace2));
157                         builder.AppendFormat ("<hr style=\"color: silver\"/>{0}</body></html>\r\n", DateTime.UtcNow);
158                 }
159
160                 string GetCustomErrorDefaultMessage ()
161                 {
162                         StringBuilder builder = new StringBuilder ();
163                         WriteFileTop (builder, "Runtime Error");
164                         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). It could, however, be viewed by browsers running on the local server machine.</p>
165 <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>
166 <table class=""sampleCode""><tr><td><pre>
167
168 &lt;!-- Web.Config Configuration File --&gt;
169
170 &lt;configuration&gt;
171     &lt;system.web&gt;
172
173         &lt;customErrors mode=&quot;Off&quot;/&gt;
174     &lt;/system.web&gt;
175 &lt;/configuration&gt;</pre>
176     </td></tr></table><br/>
177 <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>
178 <table class=""sampleCode""><tr><td><pre>
179 &lt;!-- Web.Config Configuration File --&gt;
180
181 &lt;configuration&gt;
182     &lt;system.web&gt;
183         &lt;customErrors mode=&quot;RemoteOnly&quot; defaultRedirect=&quot;mycustompage.htm&quot;/&gt;
184
185     &lt;/system.web&gt;
186 &lt;/configuration&gt;</pre></td></tr></table>");
187                         WriteFileBottom (builder, null, null);
188                         return builder.ToString ();
189                 }
190                 
191                 string GetDefaultErrorMessage ()
192                 {
193                         Exception ex, baseEx;
194                         ex = baseEx = GetBaseException ();
195                         if (ex == null)
196                                 ex = this;
197
198                         StringBuilder builder = new StringBuilder ();
199                         WriteFileTop (builder, String.Format ("Error{0}", http_code != 0 ? " " + http_code : String.Empty));
200                         builder.AppendFormat ("<h2><em>{0}</em></h2>\r\n", HtmlEncode (ex.Message));
201                         builder.AppendFormat ("<p><strong>Description: </strong>{0}</p>\r\n", HtmlEncode (Description));
202                         builder.Append ("<p><strong>Error Message: </strong>");
203                         if (http_code != 0)
204                                 builder.AppendFormat ("HTTP {0}. ", http_code);
205                         builder.AppendFormat ("{0}: {1}\r\n</p>\r\n", ex.GetType ().FullName, HtmlEncode (ex.Message));
206
207                         if (baseEx != null) {
208                                 builder.AppendFormat ("<p><strong>Stack Trace: </strong></p>");
209                                 builder.Append ("<table summary=\"Stack Trace\" class=\"sampleCode\">\r\n<tr><td>");
210                                 WriteTextAsCode (builder, baseEx.ToString ());
211                                 builder.Append ("</td></tr>\r\n</table>\r\n");
212                         }
213                         WriteFileBottom (builder,
214                                          this.ToString (),
215                                          null
216                         );
217                         
218                         return builder.ToString ();
219                 }
220
221                 static string HtmlEncode (string s)
222                 {
223                         if (s == null)
224                                 return s;
225
226                         string res = HttpUtility.HtmlEncode (s);
227                         return res.Replace ("\r\n", "<br />");
228                 }
229
230                 string GetHtmlizedErrorMessage ()
231                 {
232                         StringBuilder builder = new StringBuilder ();
233                         HtmlizedException exc = (HtmlizedException) this.InnerException;
234                         WriteFileTop (builder, exc.Title);
235                         builder.AppendFormat ("<h2><em>{0}</em></h2>\r\n", exc.Title);
236                         builder.AppendFormat ("<p><strong>Description: </strong>{0}\r\n</p>\r\n", HtmlEncode (exc.Description));
237                         string errorMessage = HtmlEncode (exc.ErrorMessage).Replace ("\n", "<br/>");
238                         builder.AppendFormat ("<p><strong>Error message: </strong></p><p>{0}</p>", errorMessage);
239
240                         if (exc.FileName != null)
241                                 builder.AppendFormat ("<p><strong>File name: </strong> {0}</p>", HtmlEncode (exc.FileName));
242
243                         if (exc.FileText != null) {
244                                 if (exc.SourceFile != exc.FileName)
245                                         builder.AppendFormat ("<p><strong>Source File: </strong>{0}</p>", exc.SourceFile);
246
247                                 if (exc is ParseException) {
248                                         builder.Append ("<p>&nbsp;&nbsp;&nbsp;&nbsp;<strong>Line: </strong>");
249                                         builder.Append (exc.ErrorLines [0]);
250                                         builder.Append ("</p>");
251                                 }
252
253                                 if (exc is ParseException) {
254                                         builder.Append ("<strong>Source Error: </strong>\r\n");
255                                         builder.Append ("<table summary=\"Source error\" class=\"sampleCode\">\r\n<tr><td>");
256                                         WriteSource (builder, exc);
257                                         builder.Append ("</td></tr>\r\n</table>\r\n");
258                                 } else {
259                                         builder.Append ("<table summary=\"Source file\" class=\"sampleCode\">\r\n<tr><td>");
260                                         WriteSource (builder, exc);
261                                         builder.Append ("</td></tr>\r\n</table>\r\n");
262                                 }
263                         }                       
264
265                         WriteFileBottom (
266                                 builder,
267                                 exc.ToString (),
268                                 null
269                         );
270                         
271                         return builder.ToString ();
272                 }
273
274                 static void WriteTextAsCode (StringBuilder builder, string text)
275                 {
276                         builder.AppendFormat ("<pre>{0}</pre>", HtmlEncode (text));
277                 }
278
279 #if TARGET_J2EE
280                 static void WriteSource (StringBuilder builder, HtmlizedException e)
281                 {
282                         builder.Append ("<pre>");
283                         WritePageSource (builder, e);
284                         builder.Append ("</pre>\r\n");
285                 }
286
287 #else
288                 static void WriteSource (StringBuilder builder, HtmlizedException e)
289                 {
290                         builder.Append ("<pre>");
291                         if (e is CompilationException)
292                                 WriteCompilationSource (builder, e);
293                         else
294                                 WritePageSource (builder, e);
295
296                         builder.Append ("</pre>\r\n");
297                 }
298 #endif
299                 
300                 static void WriteCompilationSource (StringBuilder builder, HtmlizedException e)
301                 {
302                         int [] a = e.ErrorLines;
303                         string s;
304                         int line = 0;
305                         int index = 0;
306                         int errline = 0;
307
308                         if (a != null && a.Length > 0)
309                                 errline = a [0];
310                         
311                         TextReader reader = new StringReader (e.FileText);
312                         while ((s = reader.ReadLine ()) != null) {
313                                 line++;
314
315                                 if (errline == line)
316                                         builder.Append ("<span style=\"color: red\">");
317
318                                 builder.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
319
320                                 if (line == errline) {
321                                         builder.Append ("</span>");
322                                         errline = (++index < a.Length) ? a [index] : 0;
323                                 }
324                         }
325                 }
326
327                 static void WritePageSource (StringBuilder builder, HtmlizedException e)
328                 {
329                         string s;
330                         int line = 0;
331                         int beginerror = e.ErrorLines [0];
332                         int enderror = e.ErrorLines [1];
333                         int begin = beginerror - 3;
334                         int end = enderror + 3;
335                         if (begin <= 0)
336                                 begin = 1;
337                         
338                         TextReader reader = new StringReader (e.FileText);
339                         while ((s = reader.ReadLine ()) != null) {
340                                 line++;
341                                 if (line < begin)
342                                         continue;
343
344                                 if (line > end)
345                                         break;
346
347                                 if (beginerror == line)
348                                         builder.Append ("<span style=\"color: red\">");
349
350                                 builder.AppendFormat ("{0}\r\n", HtmlEncode (s));
351
352                                 if (enderror <= line) {
353                                         builder.Append ("</span>");
354                                         enderror = end + 1; // one shot
355                                 }
356                         }
357                 }
358                 
359                 public int GetHttpCode ()
360                 {
361                         return http_code;
362                 }
363
364                 public static HttpException CreateFromLastError (string message)
365                 {
366                         WebTrace.WriteLine ("CreateFromLastError");
367                         return new HttpException (message, 0);
368                 }
369         }
370 }
371