File: ErrorFormatter.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="ErrorFormatter.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
/*********************************
 
Class hierarchy
 
ErrorFormatter (abstract)
    UnhandledErrorFormatter
        SecurityErrorFormatter
        UseLastUnhandledErrorFormatter
        TemplatedMailRuntimeErrorFormatter
    PageNotFoundErrorFormatter
    PageForbiddenErrorFormatter
    GenericApplicationErrorFormatter
    FormatterWithFileInfo (abstract)
        ParseErrorFormatter
        ConfigErrorFormatter
    DynamicCompileErrorFormatter
        TemplatedMailCompileErrorFormatter    
    UrlAuthFailedErrorFormatter
    TraceHandlerErrorFormatter
    TemplatedMailErrorFormatterGenerator
    AuthFailedErrorFormatter
    FileAccessFailedErrorFormatter
    PassportAuthFailedErrorFormatter
 
**********************************/
 
/*
 * Object used to put together ASP.NET HTML error messages
 *
 * Copyright (c) 1999 Microsoft Corporation
 */
 
namespace System.Web {
    using System.Runtime.Serialization.Formatters;
    using System.Text;
    using System.Diagnostics;
    using System.Drawing;
    using System.Reflection;
    using System.Configuration.Assemblies;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Globalization;
    using System.Web.Hosting;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.Util;
    using System.Web.Compilation;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Text.RegularExpressions;
    using System.CodeDom.Compiler;
    using System.ComponentModel;
    using Debug=System.Web.Util.Debug;
    using System.Web.Management;
    using System.Configuration;
    using System.Security;
    using System.Security.Permissions;
 
    /*
     * This is an abstract base class from which we derive other formatters.
     */
    internal abstract class ErrorFormatter {
 
        private StringCollection _adaptiveMiscContent;
        private StringCollection _adaptiveStackTrace;
        protected bool           _dontShowVersion = false;
        internal bool            _fusionLogWritten = false;
 
        internal const string startExpandableBlock =
            "<br><div class=\"expandable\" onclick=\"OnToggleTOCLevel1('{0}')\">" +
            "{1}" +
            ":</div>\r\n" +
            "<div id=\"{0}\" style=\"display: none;\">\r\n" +
            "            <br>";
 
        internal const string startColoredSquare =
                "            <table width=100% bgcolor=\"#ffffcc\">\r\n" +
                "               <tr>\r\n" +
                "                  <td>\r\n" +
                "                      <code>";
 
 
        internal const string endColoredSquare =
            "                      </code>\r\n\r\n" +
            "                  </td>\r\n" +
            "               </tr>\r\n" +
            "            </table>\r\n\r\n";
 
        internal const string endExpandableBlock =
            "            \r\n\r\n" +
            "</div>\r\n";
 
        internal const string toggleScript = @"
        <script type=""text/javascript"">
        function OnToggleTOCLevel1(level2ID)
        {
        var elemLevel2 = document.getElementById(level2ID);
        if (elemLevel2.style.display == 'none')
        {
            elemLevel2.style.display = '';
        }
        else {
            elemLevel2.style.display = 'none';
        }
        }
        </script>
                            ";
 
        protected const string BeginLeftToRightTag = "<div dir=\"ltr\">";
        protected const string EndLeftToRightTag = "</div>";
 
        internal static bool RequiresAdaptiveErrorReporting(HttpContext context)
        {
 
            // If HostingInit failed, don't try to continue, as we are not sufficiently
            // initialized to execute this code (VSWhidbey 210495)
            if (HttpRuntime.HostingInitFailed)
                return false;
 
            HttpRequest request = (context != null) ? context.Request : null;
            if (context != null && context.WorkerRequest is System.Web.SessionState.StateHttpWorkerRequest)
                return false;
 
            // Request.Browser might throw if the configuration file has some
            // bad format.
            HttpBrowserCapabilities browser = null;
            try {
                browser = (request != null) ? request.Browser : null;
            }
            catch {
                return false;
            }
 
            if (browser != null &&
                browser["requiresAdaptiveErrorReporting"] == "true") {
                return true;
            }
            return false;
        }
 
        private Literal CreateBreakLiteral() {
            Literal breakControl = new Literal();
            breakControl.Text = "<br/>";
            return breakControl;
        }
 
        private Label CreateLabelFromText(String text) {
            Label label = new Label();
            label.Text = text;
            return label;
        }
 
        // Return error message in markup using adaptive rendering of web
        // controls.  This would also set the corresponding headers of the
        // response accordingly so content can be shown properly on devices.
        // This method has been added with the same signature of
        // GetHtmlErrorMessage for consistency.
        internal virtual string GetAdaptiveErrorMessage(HttpContext context, bool dontShowSensitiveInfo) {
 
            // This call will compute and set all the necessary properties of
            // this instance of ErrorFormatter.  Then the controls below can
            // collect info from the properties.  The returned html is safely
            // ignored.
            GetHtmlErrorMessage(dontShowSensitiveInfo);
 
            // We need to inform the Response object that adaptive error is used
            // so it can adjust the status code right before headers are written out.
            // It is because some mobile devices/browsers can display a page
            // content only if it is a normal response instead of response that
            // has error status code.
            context.Response.UseAdaptiveError = true;
 
            try {
                Page page = new ErrorFormatterPage();
                page.EnableViewState = false;
 
                HtmlForm form = new HtmlForm();
                page.Controls.Add(form);
                IParserAccessor formAdd = (IParserAccessor) form;
 
                // Display a server error text with the application name
                Label label = CreateLabelFromText(SR.GetString(SR.Error_Formatter_ASPNET_Error, HttpRuntime.AppDomainAppVirtualPath));
                label.ForeColor = Color.Red;
                label.Font.Bold = true;
                label.Font.Size = FontUnit.Large;
                formAdd.AddParsedSubObject(label);
                formAdd.AddParsedSubObject(CreateBreakLiteral());
 
                // Title
                label = CreateLabelFromText(ErrorTitle);
                label.ForeColor = Color.Maroon;
                label.Font.Bold = true;
                label.Font.Italic = true;
                formAdd.AddParsedSubObject(label);
                formAdd.AddParsedSubObject(CreateBreakLiteral());
 
                // Description
                formAdd.AddParsedSubObject(CreateLabelFromText(SR.GetString(SR.Error_Formatter_Description) + " " + Description));
                formAdd.AddParsedSubObject(CreateBreakLiteral());
 
                // Misc Title
                String miscTitle = MiscSectionTitle;
                if (!String.IsNullOrEmpty(miscTitle)) {
                    formAdd.AddParsedSubObject(CreateLabelFromText(miscTitle));
                    formAdd.AddParsedSubObject(CreateBreakLiteral());
                }
 
                // Misc Info
                StringCollection miscContent = AdaptiveMiscContent;
                if (miscContent != null && miscContent.Count > 0) {
                    foreach (String contentLine in miscContent) {
                        formAdd.AddParsedSubObject(CreateLabelFromText(contentLine));
                        formAdd.AddParsedSubObject(CreateBreakLiteral());
                    }
                }
 
                // File & line# info
                String sourceFilePath = GetDisplayPath();
                if (!String.IsNullOrEmpty(sourceFilePath)) {
                    String text = SR.GetString(SR.Error_Formatter_Source_File) + " " + sourceFilePath;
                    formAdd.AddParsedSubObject(CreateLabelFromText(text));
                    formAdd.AddParsedSubObject(CreateBreakLiteral());
 
                    text = SR.GetString(SR.Error_Formatter_Line) + " " + SourceFileLineNumber;
                    formAdd.AddParsedSubObject(CreateLabelFromText(text));
                    formAdd.AddParsedSubObject(CreateBreakLiteral());
                }
 
                // Stack trace info
                StringCollection stackTrace = AdaptiveStackTrace;
                if (stackTrace != null && stackTrace.Count > 0) {
                    foreach (String stack in stackTrace) {
                        formAdd.AddParsedSubObject(CreateLabelFromText(stack));
                        formAdd.AddParsedSubObject(CreateBreakLiteral());
                    }
                }
 
                // Temporarily use a string writer to capture the output and
                // return it accordingly.
                StringWriter stringWriter = new StringWriter(CultureInfo.CurrentCulture);
                TextWriter textWriter = context.Response.SwitchWriter(stringWriter);
                page.ProcessRequest(context);
                context.Response.SwitchWriter(textWriter);
 
                return stringWriter.ToString();
            }
            catch {
                return GetStaticErrorMessage(context);
            }
        }
 
        private string GetPreferredRenderingType(HttpContext context) {
            HttpRequest request = (context != null) ? context.Request : null;
 
            // Request.Browser might throw if the configuration file has some
            // bad format.
            HttpBrowserCapabilities browser = null;
            try {
                browser = (request != null) ? request.Browser : null;
            }
            catch {
                return String.Empty;
            }
            return ((browser != null) ? browser["preferredRenderingType"] : String.Empty);
        }
 
        private string GetStaticErrorMessage(HttpContext context) {
            string preferredRenderingType = GetPreferredRenderingType(context);
            Debug.Assert(preferredRenderingType != null);
 
            string errorMessage;
            if (StringUtil.StringStartsWithIgnoreCase(preferredRenderingType, "xhtml")) {
                errorMessage = FormatStaticErrorMessage(StaticErrorFormatterHelper.XhtmlErrorBeginTemplate,
                                                        StaticErrorFormatterHelper.XhtmlErrorEndTemplate);
            }
            else if (StringUtil.StringStartsWithIgnoreCase(preferredRenderingType, "wml")) {
                errorMessage = FormatStaticErrorMessage(StaticErrorFormatterHelper.WmlErrorBeginTemplate,
                                                        StaticErrorFormatterHelper.WmlErrorEndTemplate);
 
                // VSWhidbey 161754: In the case that headers have been written,
                // we should try to set the content type only if needed.
                const string wmlContentType = "text/vnd.wap.wml";
                if (String.Compare(context.Response.ContentType, 0,
                                   wmlContentType, 0, wmlContentType.Length,
                                   StringComparison.OrdinalIgnoreCase) != 0) {
                    context.Response.ContentType = wmlContentType;
                }
            }
            else {
                errorMessage = FormatStaticErrorMessage(StaticErrorFormatterHelper.ChtmlErrorBeginTemplate,
                                                        StaticErrorFormatterHelper.ChtmlErrorEndTemplate);
            }
            return errorMessage;
        }
 
        private string FormatStaticErrorMessage(string errorBeginTemplate,
                                                string errorEndTemplate) {
            StringBuilder errorContent = new StringBuilder();
 
            // Server error text with the application name and Title
            string errorHeader = SR.GetString(SR.Error_Formatter_ASPNET_Error, HttpRuntime.AppDomainAppVirtualPath);
            errorContent.Append(String.Format(CultureInfo.CurrentCulture, errorBeginTemplate, errorHeader, ErrorTitle));
 
            // Description
            errorContent.Append(SR.GetString(SR.Error_Formatter_Description) + " " + Description);
            errorContent.Append(StaticErrorFormatterHelper.Break);
 
            // Misc Title
            String miscTitle = MiscSectionTitle;
            if (miscTitle != null && miscTitle.Length > 0) {
                errorContent.Append(miscTitle);
                errorContent.Append(StaticErrorFormatterHelper.Break);
            }
 
            // Misc Info
            StringCollection miscContent = AdaptiveMiscContent;
            if (miscContent != null && miscContent.Count > 0) {
                foreach (String contentLine in miscContent) {
                    errorContent.Append(contentLine);
                    errorContent.Append(StaticErrorFormatterHelper.Break);
                }
            }
 
            // File & line# info
            String sourceFilePath = GetDisplayPath();
            if (!String.IsNullOrEmpty(sourceFilePath)) {
                String text = SR.GetString(SR.Error_Formatter_Source_File) + " " + sourceFilePath;
                errorContent.Append(text);
                errorContent.Append(StaticErrorFormatterHelper.Break);
 
                text = SR.GetString(SR.Error_Formatter_Line) + " " + SourceFileLineNumber;
                errorContent.Append(text);
                errorContent.Append(StaticErrorFormatterHelper.Break);
            }
 
            // Stack trace info
            StringCollection stackTrace = AdaptiveStackTrace;
            if (stackTrace != null && stackTrace.Count > 0) {
                foreach (String stack in stackTrace) {
                    errorContent.Append(stack);
                    errorContent.Append(StaticErrorFormatterHelper.Break);
                }
            }
 
            errorContent.Append(errorEndTemplate);
            return errorContent.ToString();
        }
 
        internal string GetErrorMessage() {
            return GetErrorMessage(HttpContext.Current, true);
        }
 
        // Return error message by checking if adaptive error formatting
        // should be used.
        internal virtual string GetErrorMessage(HttpContext context, bool dontShowSensitiveInfo) {
            if (RequiresAdaptiveErrorReporting(context)) {
                return GetAdaptiveErrorMessage(context, dontShowSensitiveInfo);
            }
            return GetHtmlErrorMessage(dontShowSensitiveInfo);
        }
 
        internal /*public*/ string GetHtmlErrorMessage() {
            return GetHtmlErrorMessage(true);
        }
 
        internal /*public*/ string GetHtmlErrorMessage(bool dontShowSensitiveInfo) {
 
            // Give the formatter a chance to prepare its state
            PrepareFormatter();
 
            StringBuilder sb = new StringBuilder();
 
            // 
 
 
            sb.Append("<!DOCTYPE html>\r\n");
            sb.Append("<html");
 
            // VSWhidbey 477678: Honor right to left language text format.
            if (IsTextRightToLeft) {
                sb.Append(" dir=\"rtl\"");
            }
 
            sb.Append(">\r\n");
            sb.Append("    <head>\r\n");
            sb.Append("        <title>" + ErrorTitle + "</title>\r\n");
            sb.Append("        <meta name=\"viewport\" content=\"width=device-width\" />\r\n");
            sb.Append("        <style>\r\n");
            sb.Append("         body {font-family:\"Verdana\";font-weight:normal;font-size: .7em;color:black;} \r\n");
            sb.Append("         p {font-family:\"Verdana\";font-weight:normal;color:black;margin-top: -5px}\r\n");
            sb.Append("         b {font-family:\"Verdana\";font-weight:bold;color:black;margin-top: -5px}\r\n");
            sb.Append("         H1 { font-family:\"Verdana\";font-weight:normal;font-size:18pt;color:red }\r\n");
            sb.Append("         H2 { font-family:\"Verdana\";font-weight:normal;font-size:14pt;color:maroon }\r\n");
            sb.Append("         pre {font-family:\"Consolas\",\"Lucida Console\",Monospace;font-size:11pt;margin:0;padding:0.5em;line-height:14pt}\r\n");
            sb.Append("         .marker {font-weight: bold; color: black;text-decoration: none;}\r\n");
            sb.Append("         .version {color: gray;}\r\n");
            sb.Append("         .error {margin-bottom: 10px;}\r\n");
            sb.Append("         .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:pointer; }\r\n");
            sb.Append("         @media screen and (max-width: 639px) {\r\n");
            sb.Append("          pre { width: 440px; overflow: auto; white-space: pre-wrap; word-wrap: break-word; }\r\n");
            sb.Append("         }\r\n");
            sb.Append("         @media screen and (max-width: 479px) {\r\n");
            sb.Append("          pre { width: 280px; }\r\n");
            sb.Append("         }\r\n");
            sb.Append("        </style>\r\n");
            sb.Append("    </head>\r\n\r\n");
            sb.Append("    <body bgcolor=\"white\">\r\n\r\n");
            sb.Append("            <span><H1>" + SR.GetString(SR.Error_Formatter_ASPNET_Error, HttpRuntime.AppDomainAppVirtualPath) + "<hr width=100% size=1 color=silver></H1>\r\n\r\n");
            sb.Append("            <h2> <i>" + ErrorTitle + "</i> </h2></span>\r\n\r\n");
            sb.Append("            <font face=\"Arial, Helvetica, Geneva, SunSans-Regular, sans-serif \">\r\n\r\n");
 
            // Top-level description
            sb.Append("            <b> " + SR.GetString(SR.Error_Formatter_Description) + " </b>" + Description + "\r\n");
            sb.Append("            <br><br>\r\n\r\n");
 
            // Error details
            WriteErrorDetails(sb, dontShowSensitiveInfo);
 
            // Footer
            if (!(dontShowSensitiveInfo || _dontShowVersion)) {  // don't show version for security reasons
                sb.Append("            <hr width=100% size=1 color=silver>\r\n\r\n");
                sb.Append("            <b>" + SR.GetString(SR.Error_Formatter_Version) + "</b>&nbsp;" +
                                       SR.GetString(SR.Error_Formatter_CLR_Build) + VersionInfo.ClrVersion +
                                       SR.GetString(SR.Error_Formatter_ASPNET_Build) + VersionInfo.EngineVersion + "\r\n\r\n");
            }
            sb.Append("            </font>\r\n\r\n");
            sb.Append("    </body>\r\n");
            sb.Append("</html>\r\n");
 
            sb.Append(PostMessage);
 
            return sb.ToString();
        }
 
        internal void WriteErrorDetails(StringBuilder sb, bool dontShowSensitiveInfo) {
 
            // Error Message
            if (MiscSectionTitle != null) {
                sb.Append("            <b> " + MiscSectionTitle + ": </b>" + MiscSectionContent + "<br><br>\r\n\r\n");
            }
 
            // Source Code Box
            WritePrimaryBox(sb, dontShowSensitiveInfo);
 
            // Additional config lines
            ConfigurationErrorsException configErrors = Exception as ConfigurationErrorsException;
            if (configErrors != null && configErrors.Errors.Count > 1) {
                sb.Append(String.Format(CultureInfo.InvariantCulture, startExpandableBlock, "additionalConfigurationErrors",
                    SR.GetString(SR.TmplConfigurationAdditionalError)));
                sb.Append(startColoredSquare + "<pre>");
 
                //
                // Get the configuration message as though there were user code on the stack,
                // so that the full path to the configuration file is not shown if the app
                // does not have PathDiscoveryPermission.
                // 
                bool revertPermitOnly = false;
                try {
                    PermissionSet ps = HttpRuntime.NamedPermissionSet;
                    if (ps != null) {
                        ps.PermitOnly();
                        revertPermitOnly = true;
                    }
                    
                    int errorNumber = 0;
                    foreach(ConfigurationException configurationError in configErrors.Errors) {
                        if (errorNumber > 0) {
                            sb.Append(configurationError.Message);
                            sb.Append("<BR/>\r\n");
                        }
 
                        errorNumber++;
                    }
                }
                finally {
                    if (revertPermitOnly) {
                        CodeAccessPermission.RevertPermitOnly();
                    }
                }
 
                sb.Append("</pre>" + endColoredSquare);
                sb.Append(endExpandableBlock);
                sb.Append(toggleScript);
            }
 
            // FusionLog
            // If it's a FileNotFoundException/FileLoadException/BadImageFormatException with a FusionLog,
            // write it out (ASURT 83587)
            if (!dontShowSensitiveInfo && Exception != null) {
                // (Only display the fusion log in medium or higher (ASURT 126827)
                if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
                    WriteFusionLogWithAssert(sb);
                }
            }
 
            // Stack Trace Box
            WriteSecondaryBox(sb, dontShowSensitiveInfo);
        }
 
        protected virtual void WritePrimaryBox(StringBuilder sb, bool dontShowSensitiveInfo) {
            WriteColoredSquare(sb, ColoredSquareTitle, ColoredSquareDescription, ColoredSquareContent, WrapColoredSquareContentLines);
            if (ShowSourceFileInfo) {
                string displayPath = GetDisplayPath();
                if (displayPath == null)
                    displayPath = SR.GetString(SR.Error_Formatter_No_Source_File);
                sb.Append("            <b> " + SR.GetString(SR.Error_Formatter_Source_File) + " </b> " + displayPath + "<b> &nbsp;&nbsp; " + SR.GetString(SR.Error_Formatter_Line) + " </b> " + SourceFileLineNumber + "\r\n");
                sb.Append("            <br><br>\r\n\r\n");
            }
        }
 
        protected virtual void WriteSecondaryBox(StringBuilder sb, bool dontShowSensitiveInfo) {
            WriteColoredSquare(sb, ColoredSquare2Title, ColoredSquare2Description, ColoredSquare2Content, false);
        }
 
        [PermissionSet(SecurityAction.Assert, Unrestricted=true)]
        private void WriteFusionLogWithAssert(StringBuilder sb) {
            for (Exception e = Exception; e != null; e = e.InnerException) {
                string fusionLog = null;
                string filename = null;
                FileNotFoundException fnfException = e as FileNotFoundException;
                if (fnfException != null) {
                    fusionLog = fnfException.FusionLog;
                    filename = fnfException.FileName;
                }
                FileLoadException flException = e as FileLoadException;
                if (flException != null) {
                    fusionLog = flException.FusionLog;
                    filename = flException.FileName;
                }
                BadImageFormatException bifException = e as BadImageFormatException;
                if (bifException != null) {
                    fusionLog = bifException.FusionLog;
                    filename = bifException.FileName;
                }
                if (!String.IsNullOrEmpty(fusionLog)) {
                    WriteColoredSquare(sb,
                                       SR.GetString(SR.Error_Formatter_FusionLog),
                                       SR.GetString(SR.Error_Formatter_FusionLogDesc, filename),
                                       HttpUtility.HtmlEncode(fusionLog),
                                       false /*WrapColoredSquareContentLines*/);
                    _fusionLogWritten = true;
                    break;
                }
            }
        }
 
        protected void WriteColoredSquare(StringBuilder sb, string title, string description,
            string content, bool wrapContentLines) {
            if (title != null) {
                sb.Append("            <b>" + title + ":</b> " + description + "<br><br>\r\n\r\n");
                sb.Append(startColoredSquare);
                if (!wrapContentLines)
                    sb.Append("<pre>");
                sb.Append("\r\n\r\n");
                sb.Append(content);
                if (!wrapContentLines)
                    sb.Append("</pre>");
                sb.Append(endColoredSquare);
                sb.Append("            <br>\r\n\r\n");
            }
        }
 
        internal /*public*/ virtual void PrepareFormatter() {
            // VSWhidbey 139210: ErrorFormatter object might be reused and
            // the properties would be gone through again.  So we need to
            // clear the adaptive error content to avoid duplicate content.
            if (_adaptiveMiscContent != null) {
                _adaptiveMiscContent.Clear();
            }
 
            if (_adaptiveStackTrace != null) {
                _adaptiveStackTrace.Clear();
            }
        }
 
        /*
         * Return the associated exception object (if any)
         */
        protected virtual Exception Exception {
            get { return null; }
        }
 
        /*
         * Return the type of error.  e.g. "Compilation Error."
         */
        protected abstract string ErrorTitle {
            get;
        }
 
        /*
         * Return a description of the error
         * e.g. "An error occurred during the compilation of a resource required to service"
         */
        protected abstract string Description {
            get;
        }
 
        /*
         * A section used differently by different types of errors (title)
         * e.g. "Compiler Error Message"
         * e.g. "Exception Details"
         */
        protected abstract string MiscSectionTitle {
            get;
        }
 
        /*
         * A section used differently by different types of errors (content)
         * e.g. "BC30198: Expected: )"
         * e.g. "System.NullReferenceException"
         */
        protected abstract string MiscSectionContent {
            get;
        }
 
        /*
         * e.g. "Source Error"
         */
        protected virtual string ColoredSquareTitle {
            get { return null;}
        }
 
        /*
         * Optional text between color square title and the color square itself
         */
        protected virtual string ColoredSquareDescription {
            get { return null;}
        }
 
        /*
         * e.g. a piece of source code with the error context
         */
        protected virtual string ColoredSquareContent {
            get { return null;}
        }
 
        /*
         * If false, use a <pre></pre> tag around it
         */
        protected virtual bool WrapColoredSquareContentLines {
            get { return false;}
        }
 
        /*
         * e.g. "Source Error"
         */
        protected virtual string ColoredSquare2Title {
            get { return null;}
        }
 
        /*
         * Optional text between color square title and the color square itself
         */
        protected virtual string ColoredSquare2Description {
            get { return null;}
        }
 
        /*
         * e.g. a piece of source code with the error context
         */
        protected virtual string ColoredSquare2Content {
            get { return null;}
        }
 
        /*
         * Misc content which will be shown to mobile devices
         * e.g. compile error code
         */
        protected virtual StringCollection AdaptiveMiscContent {
            get {
                if (_adaptiveMiscContent == null) {
                    _adaptiveMiscContent = new StringCollection();
                }
                return _adaptiveMiscContent;
            }
        }
 
        /*
         * Exception stack trace which will be shown to mobile devices
         * e.g. stack trace of a runtime error
         */
        protected virtual StringCollection AdaptiveStackTrace {
            get {
                if (_adaptiveStackTrace == null) {
                    _adaptiveStackTrace = new StringCollection();
                }
                return _adaptiveStackTrace;
            }
        }
 
        /*
         * Determines whether SourceFileName and SourceFileLineNumber will be used
         */
        protected abstract bool ShowSourceFileInfo {
            get;
        }
 
        /*
         * e.g. d:\samples\designpreview\test.aspx
         */
        protected virtual string PhysicalPath {
            get { return null;}
        }
 
        /*
         * e.g. /myapp/test.aspx
         */
        protected virtual string VirtualPath {
            get { return null;}
        }
 
        /*
         * The line number in the source file
         */
        protected virtual int SourceFileLineNumber {
            get { return 0;}
        }
 
        protected virtual String PostMessage {
            get { return null; }
        }
 
        /*
         * Does this error have only information that we want to
         * show over the web to random users?
         */
        internal virtual bool CanBeShownToAllUsers {
            get { return false;}
        }
 
        // VSWhidbey 477678: Respect current language text format that is right
        // to left.  To be used by subclasses who need to adjust text format for
        // code area accordingly.
        protected static bool IsTextRightToLeft {
            get {
                return CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
            }
        }
 
 
        protected string WrapWithLeftToRightTextFormatIfNeeded(string content) {
            if (IsTextRightToLeft) {
                content = BeginLeftToRightTag + content + EndLeftToRightTag;
            }
            return content;
        }
 
        // Make an HTTP line pragma from a virtual path
        internal static string MakeHttpLinePragma(string virtualPath) {
            string server = "http://server";
            // We should only append a "/" if the virtual path does not
            // already start with "/". Otherwise, we end up with double
            // slashes, eg http://server//vpp/foo.aspx , and this breaks
            // the VirtualPathProvider. (DevDiv 157238)
            if (virtualPath != null && !virtualPath.StartsWith("/", StringComparison.Ordinal)) {
                server += "/";
            }
 
            return (new Uri(server + virtualPath)).ToString();
        }
 
        internal static string GetSafePath(string linePragma) {
 
            // First, check if it's an http line pragma
            string virtualPath = GetVirtualPathFromHttpLinePragma(linePragma);
 
            // If so, just return the virtual path
            if (virtualPath != null)
                return virtualPath;
 
            // If not, it must be a physical path, which we need to make safe
            return HttpRuntime.GetSafePath(linePragma);
        }
 
        internal static string GetVirtualPathFromHttpLinePragma(string linePragma) {
 
            if (String.IsNullOrEmpty(linePragma))
                return null;
 
            try {
                Uri uri = new Uri(linePragma);
                if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
                    return uri.LocalPath;
            }
            catch {}
 
            return null;
        }
 
        internal static string ResolveHttpFileName(string linePragma) {
 
            // When running under VS debugger, we use URL's instead of paths in our #line pragmas.
            // When we detect this situation, we need to do a MapPath to get back to the file name (ASURT 76211/114867)
 
            string virtualPath = GetVirtualPathFromHttpLinePragma(linePragma);
 
            // If we didn't detect a virtual path, just return the input
            if (virtualPath == null)
                return linePragma;
 
            return HostingEnvironment.MapPathInternal(virtualPath);
        }
 
        /*
         * This can be either a virtual or physical path, depending on what's available
         */
        private string GetDisplayPath() {
 
            if (VirtualPath != null)
                return VirtualPath;
 
            // It used to be an Assert on the following check but since
            // adaptive error rendering uses this method where both
            // VirtualPath and PhysicalPath might not set, it is changed to
            // an if statement.
            if (PhysicalPath != null)
                return HttpRuntime.GetSafePath(PhysicalPath);
 
            return null;
        }
    }
 
    /*
     * This formatter is used for runtime exceptions that don't fall into a
     * specific category.
     */
    internal class UnhandledErrorFormatter : ErrorFormatter {
        protected Exception _e;
        protected Exception _initialException;
        protected ArrayList _exStack = new ArrayList();
        protected string _physicalPath;
        protected int _line;
        private string _coloredSquare2Content;
        private bool _fGeneratedCodeOnStack;
        protected String _message;
        protected String _postMessage;
 
        internal UnhandledErrorFormatter(Exception e) : this(e, null, null){
        }
 
        internal UnhandledErrorFormatter(Exception e, String message, String postMessage) {
            _message = message;
            _postMessage = postMessage;
            _e = e;
        }
 
        internal /*public*/ override void PrepareFormatter() {
 
            // Build a stack of exceptions
            for (Exception e = _e; e != null; e = e.InnerException) {
                _exStack.Add(e);
 
                // Keep track of the initial exception (first one thrown)
                _initialException = e;
            }
 
            // Get the Square2Content first so the line number gets calculated
            _coloredSquare2Content = ColoredSquare2Content;
        }
 
        protected override Exception Exception {
            get { return _e; }
        }
 
        protected override string ErrorTitle {
            get {
                // Use the exception's message if there is one
                string msg = _initialException.Message;
                if (!String.IsNullOrEmpty(msg))
                    return HttpUtility.FormatPlainTextAsHtml(msg);
 
                // Otherwise, use some default string
                return SR.GetString(SR.Unhandled_Err_Error);
            }
        }
 
        protected override string Description {
            get {
                if (_message != null) {
                    return _message;
                }
                else {
                    return SR.GetString(SR.Unhandled_Err_Desc);
                }
            }
        }
 
        protected override string MiscSectionTitle {
            get { return SR.GetString(SR.Unhandled_Err_Exception_Details);}
        }
 
        protected override string MiscSectionContent {
            get {
                string exceptionName = _initialException.GetType().FullName;
                StringBuilder msg = new StringBuilder(exceptionName);
                string adaptiveMiscLine = exceptionName;
 
                if (_initialException.Message != null) {
                    string errorMessage = HttpUtility.FormatPlainTextAsHtml(_initialException.Message);
                    msg.Append(": ");
                    msg.Append(errorMessage);
                    adaptiveMiscLine += ": " + errorMessage;
                }
                AdaptiveMiscContent.Add(adaptiveMiscLine);
 
                if (_initialException is UnauthorizedAccessException) {
                    msg.Append("\r\n<br><br>");
                    String errDesc = SR.GetString(SR.Unauthorized_Err_Desc1);
                    errDesc = HttpUtility.HtmlEncode(errDesc);
                    msg.Append(errDesc);
                    AdaptiveMiscContent.Add(errDesc);
 
                    msg.Append("\r\n<br><br>");
                    errDesc = SR.GetString(SR.Unauthorized_Err_Desc2);
                    errDesc = HttpUtility.HtmlEncode(errDesc);
                    msg.Append(errDesc);
                    AdaptiveMiscContent.Add(errDesc);
                }
                else if (_initialException is HostingEnvironmentException) {
                    String details = ((HostingEnvironmentException)_initialException).Details;
 
                    if (!String.IsNullOrEmpty(details)) {
                        msg.Append("\r\n<br><br><b>");
                        msg.Append(details);
                        msg.Append("</b>");
                        AdaptiveMiscContent.Add(details);
                    }
                }
 
                return msg.ToString();
            }
        }
 
        protected override string ColoredSquareTitle {
            get { return SR.GetString(SR.TmplCompilerSourceSecTitle);}
        }
 
        protected override string ColoredSquareContent {
            get {
 
                // If we couldn't get line info for the error, display a standard message
                if (_physicalPath == null) {
 
                    const string BeginLeftToRightMarker = "BeginMarker";
                    const string EndLeftToRightMarker = "EndMarker";
                    bool setLeftToRightMarker = false;
 
                    // The error text depends on whether .aspx code was found on the stack
                    // Also, if trust is less than medium, never display the message that
                    // explains how to turn on debugging, since it's not allowed (Whidbey 9176)
                    string msg;
                    if (!_fGeneratedCodeOnStack ||
                        !HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
                        msg = SR.GetString(SR.Src_not_available_nodebug);
                    }
                    else {
                        if (IsTextRightToLeft) {
                            setLeftToRightMarker = true;
                        }
 
                        // Because the resource string has both normal language text and config/code samples,
                        // left-to-right markup tags need to be wrapped around the config/code samples if
                        // right to left language format is being used.
                        //
                        // Note that the retrieved resource string will be passed to the call
                        // HttpUtility.FormatPlainTextAsHtml(), which does HtmlEncode.  In order to preserve
                        // the left-to-right markup tags, the resource string has been added with markers
                        // that identify the beginnings and ends of config/code samples.  After
                        // FormatPlainTextAsHtml() is called, and the markers will be replaced with
                        // left-to-right markup tags below.
                        msg = SR.GetString(SR.Src_not_available,
                                           ((setLeftToRightMarker) ? BeginLeftToRightMarker : string.Empty),
                                           ((setLeftToRightMarker) ? EndLeftToRightMarker : string.Empty),
                                           ((setLeftToRightMarker) ? BeginLeftToRightMarker : string.Empty),
                                           ((setLeftToRightMarker) ? EndLeftToRightMarker : string.Empty));
                    }
 
                    msg = HttpUtility.FormatPlainTextAsHtml(msg);
 
                    if (setLeftToRightMarker) {
                        // If only <div dir=ltr> was used to wrap around the left-to-right code text,
                        // the font rendering on Firefox was not good.  We use <code> in addition to
                        // the <div> tag to workaround the problem.
                        const string BeginLeftToRightTags = "</code>" + BeginLeftToRightTag + "<code>";
                        const string EndLeftToRightTags = "</code>" + EndLeftToRightTag + "<code>";
                        msg = msg.Replace(BeginLeftToRightMarker, BeginLeftToRightTags);
                        msg = msg.Replace(EndLeftToRightMarker, EndLeftToRightTags);
                    }
 
                    return msg;
                }
 
                return FormatterWithFileInfo.GetSourceFileLines(_physicalPath, Encoding.Default, null, _line);
            }
        }
 
        protected override bool WrapColoredSquareContentLines {
            // Only wrap the text if we're displaying the standard message
            get { return (_physicalPath == null);}
        }
 
        protected override string ColoredSquare2Title {
            get { return SR.GetString(SR.Unhandled_Err_Stack_Trace);}
        }
 
        protected override string ColoredSquare2Content {
            get {
                if (_coloredSquare2Content != null)
                    return _coloredSquare2Content;
 
                StringBuilder sb = new StringBuilder();
                bool addAdaptiveStackTrace = true;
                int sbBeginIndex = 0;
 
                for (int i = _exStack.Count - 1; i >=0; i--) {
                    if (i < _exStack.Count - 1)
                        sb.Append("\r\n");
 
                    Exception e = (Exception)_exStack[i];
 
                    sb.Append("[" + _exStack[i].GetType().Name);
 
                    // Display the error code if there is one
                    if ((e is ExternalException) && ((ExternalException) e).ErrorCode != 0)
                        sb.Append(" (0x" + (((ExternalException)e).ErrorCode).ToString("x", CultureInfo.CurrentCulture) + ")");
 
                    // Display the message if there is one
                    if (e.Message != null && e.Message.Length > 0)
                        sb.Append(": " + e.Message);
 
                    sb.Append("]\r\n");
 
                    // Display the stack trace
                    StackTrace st = new StackTrace(e, true /*fNeedFileInfo*/);
                    for (int j = 0; j < st.FrameCount; j++) {
 
                        if (addAdaptiveStackTrace) {
                            sbBeginIndex = sb.Length;
                        }
                        StackFrame sf = st.GetFrame(j);
 
                        MethodBase mb = sf.GetMethod();
                        Type declaringType = mb.DeclaringType;
                        string ns = String.Empty;
                        if (declaringType != null) {
 
                            // Check if this stack item is for ASP generated code (ASURT 51063).
                            // To do this, we check if the assembly lives in the codegen dir.
                            // But if the native offset is 0, it is likely that the method simply
                            // failed to JIT, in which case don't treat it as an ASP.NET stack,
                            // since no line number can ever be shown for it (VSWhidbey 87014).
                            string assemblyDir = null;
                            try {
                                // This could throw if the assembly is dynamic
                                assemblyDir = System.Web.UI.Util.GetAssemblyCodeBase(declaringType.Assembly);
                            }
                            catch {}
 
                            if (assemblyDir != null) {
                                assemblyDir = Path.GetDirectoryName(assemblyDir);
                                if (string.Compare(assemblyDir, HttpRuntime.CodegenDirInternal,
                                    StringComparison.OrdinalIgnoreCase) == 0 && sf.GetNativeOffset() > 0) {
                                    _fGeneratedCodeOnStack = true;
                                }
                            }
 
                            ns = declaringType.Namespace;
                        }
 
                        if (ns != null)
                            ns = ns + ".";
 
                        if (declaringType == null) {
                            sb.Append("   " + mb.Name + "(");
                        }
                        else {
                            sb.Append("   " + ns + declaringType.Name + "." +
                                mb.Name + "(");
                        }
 
                        ParameterInfo[] arrParams = mb.GetParameters();
 
                        for (int k = 0; k < arrParams.Length; k++) {
                            sb.Append((k != 0 ? ", " : String.Empty) + arrParams[k].ParameterType.Name + " " +
                                arrParams[k].Name);
                        }
 
                        sb.Append(")");
 
                        string fileName = GetFileName(sf);
                        if (fileName != null) {
 
                            // ASURT 114867: if it's an http path, turn it into a local path
                            fileName = ResolveHttpFileName(fileName);
                            if (fileName != null) {
 
                                // Remember the file/line number of the top level stack
                                // item for which we have symbols
                                if (_physicalPath == null && FileUtil.FileExists(fileName)) {
                                    _physicalPath = fileName;
 
                                    _line = sf.GetFileLineNumber();
                                }
 
                                sb.Append(" in " + HttpRuntime.GetSafePath(fileName) +
                                    ":" + sf.GetFileLineNumber());
                            }
                        }
                        else {
                            sb.Append(" +" + sf.GetNativeOffset());
                        }
 
                        if (addAdaptiveStackTrace) {
                            string stackTraceText = sb.ToString(sbBeginIndex,
                                                                sb.Length - sbBeginIndex);
                            AdaptiveStackTrace.Add(HttpUtility.HtmlEncode(stackTraceText));
                        }
 
                        sb.Append("\r\n");
                    }
                    // Due to size limitation, we only want to add the top
                    // stack trace for mobile devices.
                    addAdaptiveStackTrace = false;
                }
 
                _coloredSquare2Content = HttpUtility.HtmlEncode(sb.ToString());
 
                _coloredSquare2Content = WrapWithLeftToRightTextFormatIfNeeded(_coloredSquare2Content);
 
                return _coloredSquare2Content;
            }
        }
 
        // Dev10 786146: partial trust apps may not have PathDiscovery, so just pretend
        // the path is unknown.
        private string GetFileName(StackFrame sf) {
            string fileName = null;
            try {
                fileName = sf.GetFileName();
            }
            catch (SecurityException) {
            }
            return fileName;
        }
 
        protected override String PostMessage {
            get { return _postMessage; }
        }
 
        protected override bool ShowSourceFileInfo {
            get { return _physicalPath != null; }
        }
 
        protected override string PhysicalPath {
            get { return _physicalPath; }
        }
 
        protected override int SourceFileLineNumber {
            get { return _line; }
        }
    }
 
    /*
     * This formatter is used for security exceptions.
     */
    internal class SecurityErrorFormatter : UnhandledErrorFormatter {
 
        internal SecurityErrorFormatter(Exception e) : base(e) {}
 
        protected override string ErrorTitle {
            get {
                return SR.GetString(SR.Security_Err_Error);
            }
        }
 
        protected override string Description {
            get {
                // VSWhidbey 493720: Do Html encode to preserve space characters
                return HttpUtility.FormatPlainTextAsHtml(SR.GetString(SR.Security_Err_Desc));
            }
        }
    }
 
    /*
     * This formatter is used for 404: page not found errors
     */
    internal class PageNotFoundErrorFormatter : ErrorFormatter {
        protected string _htmlEncodedUrl;
        private StringCollection _adaptiveMiscContent = new StringCollection();
 
        internal PageNotFoundErrorFormatter(string url) {
            _htmlEncodedUrl = HttpUtility.HtmlEncode(url);
            _adaptiveMiscContent.Add(_htmlEncodedUrl);
        }
 
        protected override string ErrorTitle {
            get { return SR.GetString(SR.NotFound_Resource_Not_Found);}
        }
 
        protected override string Description {
            get { return HttpUtility.FormatPlainTextAsHtml(SR.GetString(SR.NotFound_Http_404));}
        }
 
        protected override string MiscSectionTitle {
            get { return SR.GetString(SR.NotFound_Requested_Url);}
        }
 
        protected override string MiscSectionContent {
            get { return _htmlEncodedUrl;}
        }
 
        protected override StringCollection AdaptiveMiscContent {
            get { return _adaptiveMiscContent;}
        }
 
        protected override bool ShowSourceFileInfo {
            get { return false;}
        }
 
        internal override bool CanBeShownToAllUsers {
            get { return true;}
        }
    }
 
    /*
     * This formatter is used for 403: forbidden
     */
    internal class PageForbiddenErrorFormatter : ErrorFormatter {
        protected string _htmlEncodedUrl;
        private StringCollection _adaptiveMiscContent = new StringCollection();
        private string _description;
 
        internal PageForbiddenErrorFormatter(string url): this(url, null) {
        }
 
        internal PageForbiddenErrorFormatter(string url, string description) {
            _htmlEncodedUrl = HttpUtility.HtmlEncode(url);
            _adaptiveMiscContent.Add(_htmlEncodedUrl);
            _description = description;
        }
 
        protected override string ErrorTitle {
            get { return SR.GetString(SR.Forbidden_Type_Not_Served);}
        }
 
        protected override string Description {
            get {
                if (_description != null) {
                    return _description;
                }
                Match m = Regex.Match(_htmlEncodedUrl, @"\.\w+$");
 
                String extMessage = String.Empty;
 
                if (m.Success)
                    extMessage = SR.GetString(SR.Forbidden_Extension_Incorrect, m.ToString());
 
                return HttpUtility.FormatPlainTextAsHtml(SR.GetString(SR.Forbidden_Extension_Desc, extMessage));
            }
        }
 
        protected override string MiscSectionTitle {
            get { return SR.GetString(SR.NotFound_Requested_Url);}
        }
 
        protected override string MiscSectionContent {
            get { return _htmlEncodedUrl;}
        }
 
        protected override StringCollection AdaptiveMiscContent {
            get { return _adaptiveMiscContent;}
        }
 
        protected override bool ShowSourceFileInfo {
            get { return false;}
        }
 
        internal override bool CanBeShownToAllUsers {
            get { return true;}
        }
    }
 
    /*
     * This formatter is used for generic errors that hide sensitive information
     * error text is sometimes different for remote vs. local machines
     */
    internal class GenericApplicationErrorFormatter : ErrorFormatter {
        private bool _local;
 
        internal GenericApplicationErrorFormatter(bool local) {
            _local = local;
        }
 
        protected override string ErrorTitle {
            get {
                return SR.GetString(SR.Generic_Err_Title);
            }
        }
 
        protected override string Description {
            get {
                return SR.GetString(
                                    _local ? SR.Generic_Err_Local_Desc
                                           : SR.Generic_Err_Remote_Desc);
            }
        }
 
        protected override string MiscSectionTitle {
            get {
                return null;
            }
        }
 
        protected override string MiscSectionContent {
            get {
                return null;
            }
        }
 
        protected override string ColoredSquareTitle {
            get {
                String detailsTitle = SR.GetString(SR.Generic_Err_Details_Title);
                AdaptiveMiscContent.Add(detailsTitle);
                return detailsTitle;
            }
        }
 
        protected override string ColoredSquareDescription {
            get {
                String detailsDesc = SR.GetString(
                                    _local ? SR.Generic_Err_Local_Details_Desc
                                           : SR.Generic_Err_Remote_Details_Desc);
                detailsDesc = HttpUtility.HtmlEncode(detailsDesc);
                AdaptiveMiscContent.Add(detailsDesc);
                return detailsDesc;
            }
        }
 
        protected override string ColoredSquareContent {
            get {
                string content = HttpUtility.HtmlEncode(SR.GetString(
                                    _local ? SR.Generic_Err_Local_Details_Sample
                                           : SR.Generic_Err_Remote_Details_Sample));
 
                return (WrapWithLeftToRightTextFormatIfNeeded(content));
            }
        }
 
        protected override string ColoredSquare2Title {
            get {
                String noteTitle = SR.GetString(SR.Generic_Err_Notes_Title);
                AdaptiveMiscContent.Add(noteTitle);
                return noteTitle;
            }
        }
 
        protected override string ColoredSquare2Description {
            get {
                String notesDesc = SR.GetString(SR.Generic_Err_Notes_Desc);
                notesDesc = HttpUtility.HtmlEncode(notesDesc);
                AdaptiveMiscContent.Add(notesDesc);
                return notesDesc;
            }
        }
 
        protected override string ColoredSquare2Content {
            get {
                string content = HttpUtility.HtmlEncode(SR.GetString(
                                    _local ? SR.Generic_Err_Local_Notes_Sample
                                           : SR.Generic_Err_Remote_Notes_Sample));
 
                return (WrapWithLeftToRightTextFormatIfNeeded(content));
            }
        }
 
        protected override bool ShowSourceFileInfo {
            get { return false;}
        }
 
        internal override bool CanBeShownToAllUsers {
            get { return true;}
        }
    }
 
    /*
    * This formatter is used when we couldn't run the normal custom error page (due to it also failing)
    */
    internal class CustomErrorFailedErrorFormatter : ErrorFormatter {
        internal CustomErrorFailedErrorFormatter() {
        }
 
        protected override string ErrorTitle {
            get { return SR.GetString(SR.Generic_Err_Title); }
        }
 
        protected override string Description {
            get { return HttpUtility.FormatPlainTextAsHtml(SR.GetString(SR.CustomErrorFailed_Err_Desc)); }
        }
 
        protected override string MiscSectionTitle {
            get { return null; }
        }
 
        protected override string MiscSectionContent {
            get { return null; }
        }
 
        protected override bool ShowSourceFileInfo {
            get { return false; }
        }
 
        internal override bool CanBeShownToAllUsers {
            get { return true; }
        }
    }
 
 
    /*
     * This is the base class for formatter that handle errors that have an
     * associated file / line number.
     */
    internal abstract class FormatterWithFileInfo : ErrorFormatter {
        protected string _virtualPath;
        protected string _physicalPath;
        protected string _sourceCode;
        protected int _line;
 
        // Number of lines before and after the error lines included in the report
        private const int errorRange = 2;
 
        /*
         * Return the text of the error line in the source file, with a few
         * lines around it.  It is returned in HTML format.
         */
        internal static string GetSourceFileLines(string fileName, Encoding encoding, string sourceCode, int lineNumber) {
 
            // Don't show any source file if the user doesn't have access to it (ASURT 122430)
            if (fileName != null && !HttpRuntime.HasFilePermission(fileName))
                return SR.GetString(SR.WithFile_No_Relevant_Line);
 
            // 
            StringBuilder sb = new StringBuilder();
 
            if (lineNumber <= 0) {
                return SR.GetString(SR.WithFile_No_Relevant_Line);
            }
 
            TextReader reader = null;
 
            // Check if it's an http line pragma, from which we can get a VirtualPath
            string virtualPath = GetVirtualPathFromHttpLinePragma(fileName);
 
            // If we got a virtual path, open a TextReader from it
            if (virtualPath != null) {
                Stream stream = VirtualPathProvider.OpenFile(virtualPath);
                if (stream != null)
                    reader = System.Web.UI.Util.ReaderFromStream(stream, System.Web.VirtualPath.Create(virtualPath));
            }
 
            try {
                // Otherwise, open the physical file
                if (reader == null && fileName != null)
                    reader = new StreamReader(fileName, encoding, true, 4096);
            }
            catch { }
 
            if (reader == null) {
                if (sourceCode == null)
                    return SR.GetString(SR.WithFile_No_Relevant_Line);
 
                // Can't open the file?  Use the dynamically generated content...
                reader = new StringReader(sourceCode);
            }
 
            try {
                bool fFoundLine = false;
 
                if (IsTextRightToLeft) {
                    sb.Append(BeginLeftToRightTag);
                }
 
                for (int i=1; ; i++) {
                    // Get the current line from the source file
                    string sourceLine = reader.ReadLine();
                    if (sourceLine == null)
                        break;
 
                    // If it's the error line, make it red
                    if (i == lineNumber)
                        sb.Append("<font color=red>");
 
                    // Is it in the range we want to display
                    if (i >= lineNumber-errorRange && i <= lineNumber+errorRange) {
                        fFoundLine = true;
                        String linestr = i.ToString("G", CultureInfo.CurrentCulture);
 
                        sb.Append(SR.GetString(SR.WithFile_Line_Num, linestr));
                        if (linestr.Length < 3)
                            sb.Append(' ', 3 - linestr.Length);
                        sb.Append(HttpUtility.HtmlEncode(sourceLine));
 
                        if (i != lineNumber+errorRange)
                            sb.Append("\r\n");
                    }
 
                    if (i == lineNumber)
                        sb.Append("</font>");
 
                    if (i>lineNumber+errorRange)
                        break;
                }
 
                if (IsTextRightToLeft) {
                    sb.Append(EndLeftToRightTag);
                }
 
                if (!fFoundLine)
                    return SR.GetString(SR.WithFile_No_Relevant_Line);
            }
            finally {
                // Make sure we always close the reader
                reader.Close();
            }
 
            return sb.ToString();
        }
 
        private string GetSourceFileLines() {
            return GetSourceFileLines(_physicalPath, SourceFileEncoding, _sourceCode, _line);
        }
 
        internal FormatterWithFileInfo(string virtualPath, string physicalPath,
            string sourceCode, int line) {
 
            _virtualPath = virtualPath;
            _physicalPath = physicalPath;
 
            if (sourceCode == null && _physicalPath == null && _virtualPath != null) {
 
                // Make sure _virtualPath is really a virtual path.  Sometimes,
                // it can actually be a physical path, in which case we keep
                // it as is.
                if (UrlPath.IsValidVirtualPathWithoutProtocol(_virtualPath))
                    _physicalPath = HostingEnvironment.MapPath(_virtualPath);
                else
                    _physicalPath = _virtualPath;
            }
 
            _sourceCode = sourceCode;
            _line = line;
        }
 
        protected virtual Encoding SourceFileEncoding {
            get { return Encoding.Default; }
        }
 
        protected override string ColoredSquareContent {
            get { return GetSourceFileLines();}
        }
 
        protected override bool ShowSourceFileInfo {
            get { return true;}
        }
 
        protected override string PhysicalPath {
            get { return _physicalPath;}
        }
 
        protected override string VirtualPath {
            get { return _virtualPath;}
        }
 
        protected override int SourceFileLineNumber {
            get { return _line;}
        }
    }
 
    /*
     * Formatter used for compilation errors
     */
    internal class DynamicCompileErrorFormatter : ErrorFormatter {
 
        // Number of lines before and after the error lines included in the report
        private const int errorRange = 2;
 
        HttpCompileException _excep;
        private string _sourceFilePath = null;
        private int _sourceFileLineNumber = 0;
        protected bool _hideDetailedCompilerOutput = false;
 
        internal DynamicCompileErrorFormatter(HttpCompileException excep) {
            _excep = excep;
        }
 
        protected override Exception Exception {
            get { return _excep; }
        }
 
        protected override bool ShowSourceFileInfo {
            get {
                return false;
            }
        }
 
        protected override string ErrorTitle {
            get {
                return SR.GetString(SR.TmplCompilerErrorTitle);
            }
        }
 
        protected override string Description {
            get {
                return SR.GetString(SR.TmplCompilerErrorDesc);
            }
        }
 
        protected override string MiscSectionTitle {
            get {
                return SR.GetString(SR.TmplCompilerErrorSecTitle);
            }
        }
 
        protected override string MiscSectionContent {
            get {
                StringBuilder sb = new StringBuilder(128);
 
                CompilerResults results = _excep.ResultsWithoutDemand;
 
                // Handle fatal errors where we couldn't find an error line
                if (results.Errors.Count == 0 && results.NativeCompilerReturnValue != 0) {
                    string fatalError = SR.GetString(SR.TmplCompilerFatalError,
                                            results.NativeCompilerReturnValue.ToString("G",
                                                CultureInfo.CurrentCulture));
                    AdaptiveMiscContent.Add(fatalError);
                    sb.Append(fatalError);
                    sb.Append("<br><br>\r\n");
                }
 
                if (results.Errors.HasErrors) {
 
                    CompilerError e = _excep.FirstCompileError;
 
                    if (e != null) {
                        string htmlEncodedText = HttpUtility.HtmlEncode(e.ErrorNumber);
                        string adaptiveContentLine = htmlEncodedText;
                        sb.Append(htmlEncodedText);
                        // Don't show the error message in low trust (VSWhidbey 87012)
                        if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
                            htmlEncodedText = HttpUtility.HtmlEncode(e.ErrorText);
                            sb.Append(": ");
                            sb.Append(htmlEncodedText);
                            adaptiveContentLine += ": " + htmlEncodedText;
                        }
                        AdaptiveMiscContent.Add(adaptiveContentLine);
                        sb.Append("<br><br>\r\n");
 
                        sb.Append("<b>");
                        sb.Append(SR.GetString(SR.TmplCompilerSourceSecTitle));
                        sb.Append(":</b><br><br>\r\n");
                        sb.Append("            <table width=100% bgcolor=\"#ffffcc\">\r\n");
                        sb.Append("               <tr><td>\r\n");
                        sb.Append("               ");
                        sb.Append("               </td></tr>\r\n");
                        sb.Append("               <tr>\r\n");
                        sb.Append("                  <td>\r\n");
                        sb.Append("                      <code><pre>\r\n\r\n");
                        sb.Append(FormatterWithFileInfo.GetSourceFileLines(e.FileName, Encoding.Default, _excep.SourceCodeWithoutDemand, e.Line));
                        sb.Append("</pre></code>\r\n\r\n");
                        sb.Append("                  </td>\r\n");
                        sb.Append("               </tr>\r\n");
                        sb.Append("            </table>\r\n\r\n");
                        sb.Append("            <br>\r\n\r\n");
 
                        // display file
                        sb.Append("            <b>");
                        sb.Append(SR.GetString(SR.TmplCompilerSourceFileTitle));
                        sb.Append(":</b> ");
                        _sourceFilePath = GetSafePath(e.FileName);
                        sb.Append(HttpUtility.HtmlEncode(_sourceFilePath));
                        sb.Append("\r\n");
 
                        // display number
                        TypeConverter itc = new Int32Converter();
                        sb.Append("            &nbsp;&nbsp; <b>");
                        sb.Append(SR.GetString(SR.TmplCompilerSourceFileLine));
                        sb.Append(":</b>  ");
                        _sourceFileLineNumber = e.Line;
                        sb.Append(HttpUtility.HtmlEncode(itc.ConvertToString(_sourceFileLineNumber)));
                        sb.Append("\r\n");
                        sb.Append("            <br><br>\r\n");
                    }
                }
 
                if (results.Errors.HasWarnings) {
                    sb.Append("<br><div class=\"expandable\" onclick=\"OnToggleTOCLevel1('warningDiv')\">");
                    sb.Append(SR.GetString(SR.TmplCompilerWarningBanner));
                    sb.Append(":</div>\r\n");
                    sb.Append("<div id=\"warningDiv\" style=\"display: none;\">\r\n");
                    foreach (CompilerError e in results.Errors) {
                        if (e.IsWarning) {
                            sb.Append("<b>");
                            sb.Append(SR.GetString(SR.TmplCompilerWarningSecTitle));
                            sb.Append(":</b> ");
                            sb.Append(HttpUtility.HtmlEncode(e.ErrorNumber));
                            // Don't show the error message in low trust (VSWhidbey 87012)
                            if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
                                sb.Append(": ");
                                sb.Append(HttpUtility.HtmlEncode(e.ErrorText));
                            }
                            sb.Append("<br>\r\n");
 
                            sb.Append("<b>");
                            sb.Append(SR.GetString(SR.TmplCompilerSourceSecTitle));
                            sb.Append(":</b><br><br>\r\n");
                            sb.Append("            <table width=100% bgcolor=\"#ffffcc\">\r\n");
                            sb.Append("               <tr><td>\r\n");
                            sb.Append("               <b>");
                            sb.Append(HttpUtility.HtmlEncode(HttpRuntime.GetSafePath(e.FileName)));
                            sb.Append("</b>\r\n");
                            sb.Append("               </td></tr>\r\n");
                            sb.Append("               <tr>\r\n");
                            sb.Append("                  <td>\r\n");
                            sb.Append("                      <code><pre>\r\n\r\n");
                            sb.Append(FormatterWithFileInfo.GetSourceFileLines(e.FileName, Encoding.Default, _excep.SourceCodeWithoutDemand, e.Line));
                            sb.Append("</pre></code>\r\n\r\n");
                            sb.Append("                  </td>\r\n");
                            sb.Append("               </tr>\r\n");
                            sb.Append("            </table>\r\n\r\n");
                            sb.Append("            <br>\r\n\r\n");
                        }
                    }
                    sb.Append("</div>\r\n");
                }
 
                if (!_hideDetailedCompilerOutput) {
                    if (results.Output.Count > 0) {
                        // (Only display the compiler output in medium or higher (ASURT 126827)
                        if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
                            sb.Append(String.Format(CultureInfo.CurrentCulture, startExpandableBlock, "compilerOutputDiv",
                                SR.GetString(SR.TmplCompilerCompleteOutput)));
                            sb.Append(startColoredSquare + "<pre>");
                            foreach (string line in results.Output) {
                                sb.Append(HttpUtility.HtmlEncode(line));
                                sb.Append("\r\n");
                            }
                            sb.Append("</pre>" + endColoredSquare);
                            sb.Append(endExpandableBlock);
                        }
                    }
 
                    // If we have the generated source code, display it
                    // (Only display the source in medium or higher (ASURT 128039)
                    if (_excep.SourceCodeWithoutDemand != null &&
                        HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
 
                        sb.Append(String.Format(CultureInfo.CurrentCulture, startExpandableBlock, "dynamicCodeDiv",
                            SR.GetString(SR.TmplCompilerGeneratedFile)));
                        sb.Append(startColoredSquare + "<pre>");
 
                        string[] sourceLines = _excep.SourceCodeWithoutDemand.Split('\n');
                        int currentLine = 1;
                        foreach (string s in sourceLines) {
                            string number = currentLine.ToString("G", CultureInfo.CurrentCulture);
                            sb.Append(SR.GetString(SR.TmplCompilerLineHeader, number));
                            if (number.Length < 5) {
                                sb.Append(' ', 5 - number.Length);
                            }
                            currentLine++;
 
                            sb.Append(HttpUtility.HtmlEncode(s));
                        }
                        sb.Append("</pre>" + endColoredSquare);
                        sb.Append(endExpandableBlock);
                    }
 
                    sb.Append(toggleScript);
                }
 
                return sb.ToString();
            }
        }
 
        // This is calculated in MiscSectionContent
        protected override string PhysicalPath {
            get { return _sourceFilePath;}
        }
 
        protected override int SourceFileLineNumber {
            get { return _sourceFileLineNumber;}
        }
    }
 
    /*
     * Formatter used for parse errors
     */
    internal class ParseErrorFormatter : FormatterWithFileInfo {
        protected string _message;
        HttpParseException _excep;
        private StringCollection _adaptiveMiscContent = new StringCollection();
 
        internal ParseErrorFormatter(HttpParseException e, string virtualPath,
            string sourceCode, int line, string message)
        : base(virtualPath, null /*physicalPath*/, sourceCode, line) {
            _excep = e;
            _message = HttpUtility.FormatPlainTextAsHtml(message);
            _adaptiveMiscContent.Add(_message);
        }
 
        protected override Exception Exception {
            get { return _excep; }
        }
 
        protected override string ErrorTitle {
            get { return SR.GetString(SR.Parser_Error);}
        }
 
        protected override string Description {
            get { return SR.GetString(SR.Parser_Desc);}
        }
 
        protected override string MiscSectionTitle {
            get { return SR.GetString(SR.Parser_Error_Message);}
        }
 
        protected override string MiscSectionContent {
            get { return _message;}
        }
 
        protected override string ColoredSquareTitle {
            get { return SR.GetString(SR.Parser_Source_Error);}
        }
 
        protected override StringCollection AdaptiveMiscContent {
            get { return _adaptiveMiscContent;}
        }
    }
 
    /*
     * Formatter used for configuration errors
     */
    internal class ConfigErrorFormatter : FormatterWithFileInfo {
        protected string _message;
        private Exception _e;
        private StringCollection _adaptiveMiscContent = new StringCollection();
        private bool _allowSourceCode;
 
        internal ConfigErrorFormatter(System.Configuration.ConfigurationException e)
        : base(null /*virtualPath*/, e.Filename, null, e.Line) {
            _e = e;
            PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_PRE_PROCESSING);
            PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL);
            _message = HttpUtility.FormatPlainTextAsHtml(e.BareMessage);
            _adaptiveMiscContent.Add(_message);
        }
 
        public bool AllowSourceCode {
            get { return _allowSourceCode; }
            set { _allowSourceCode = value; }
        }
 
        protected override Encoding SourceFileEncoding {
            get { return Encoding.UTF8; }
        }
 
        protected override Exception Exception {
            get { return _e; }
        }
 
        protected override string ErrorTitle {
            get { return SR.GetString(SR.Config_Error);}
        }
 
        protected override string Description {
            get { return SR.GetString(SR.Config_Desc);}
        }
 
        protected override string MiscSectionTitle {
            get { return SR.GetString(SR.Parser_Error_Message);}
        }
 
        protected override string MiscSectionContent {
            get { return _message;}
        }
 
        protected override string ColoredSquareTitle {
            get { return SR.GetString(SR.Parser_Source_Error);}
        }
 
        protected override StringCollection AdaptiveMiscContent {
            get { return _adaptiveMiscContent;}
        }
 
        protected override string ColoredSquareContent {
            get {
                if (!AllowSourceCode) {
                    return SR.GetString(SR.Generic_Err_Remote_Desc);
                }
 
                return base.ColoredSquareContent;
            }
        }
        protected override bool WrapColoredSquareContentLines {
            get {
                if (!AllowSourceCode) {
                    return true;
                }
 
                return base.WrapColoredSquareContentLines;
            }
        }
 
        protected override void WriteSecondaryBox(StringBuilder sb, bool dontShowSensitiveInfo) {
 
            ErrorFormatter nestedFormatter = GetFormatterForInnerException((ConfigurationException)_e);
 
            // If there are no further relevant details, write the old-style box instead of the nested stuff.
            if (nestedFormatter == null || _fusionLogWritten) {
                base.WriteSecondaryBox(sb, dontShowSensitiveInfo);
                return;
            }
 
            // Special nested formatting to give more context for config builder errors
            sb.Append(String.Format(CultureInfo.InvariantCulture, startExpandableBlock, "additionalConfigErrorInfo",
                SR.GetString(SR.AdditionalConfigErrorInfo)));
            nestedFormatter.PrepareFormatter();
            nestedFormatter.WriteErrorDetails(sb, dontShowSensitiveInfo);
            sb.Append(endExpandableBlock);
            sb.Append(toggleScript);
        }
 
        // Factory method to get the appropriate type of error formatter for the given
        // exception. There are already multiple options for obtaining a top-level error
        // formatter - and they kind of assume the top exception is an HttpException, or
        // the formatter is being created for a specific context (HttpResponse, or
        // TemplatedMailProvider). This helper is ONLY intended for NESTED formatting
        // of non-config exceptions that result in config exceptions.
        private static ErrorFormatter GetFormatterForInnerException(Exception e)
        {
            ErrorFormatter exceptionFormatter = null;
 
            // First, grab the appropriate inner exception. Ignore config errors.
            Exception nested = e.InnerException;
            while (nested != null) {
                if (!(nested is ConfigurationException))
                    break;
                nested = nested.InnerException;
            }
            if (nested == null) {
                return null;
            }
 
 
            // Security errors
            if (nested is SecurityException) {
                return new SecurityErrorFormatter(e);
            }
 
            // Other known exceptions are HttpException-derived
            if (exceptionFormatter == null) {
                exceptionFormatter = HttpException.GetErrorFormatter(nested);
 
                // HttpException.GetErrorFormatter() might have returned a ConfigErrorFormatter. Don't use it.
                // If we did use it though, be sure to propagate the value of this.AllowSourceCode.
                if (exceptionFormatter is ConfigErrorFormatter) {
                    exceptionFormatter = null;
                }
            }
 
            // Unhandled/generic-unknown
            if (exceptionFormatter == null) {
                exceptionFormatter = new UnhandledErrorFormatter(nested);
            }
 
            return exceptionFormatter;
        }
    }
 
    /*
     * Formatter to allow user-specified description strings
     * use if showing inner-most exception message is not appropriate
     */
    internal class UseLastUnhandledErrorFormatter : UnhandledErrorFormatter {
 
        internal UseLastUnhandledErrorFormatter(Exception e)
            : base(e) {
        }
 
        internal /*public*/ override void PrepareFormatter() {
            base.PrepareFormatter();
 
            // use the outer-most exception instead of the inner-most in the misc section
            _initialException = Exception;
        }
    }
 
    internal class StaticErrorFormatterHelper {
        internal const string ChtmlErrorBeginTemplate = @"<html>
<body>
<form>
<font color=""Red"" size=""5"">{0}</font><br/>
<font color=""Maroon"">{1}</font><br/>
";
        internal const string ChtmlErrorEndTemplate = @"</form>
</body>
</html>";
 
        internal const string WmlErrorBeginTemplate = @"<?xml version='1.0'?>
<!DOCTYPE wml PUBLIC '-//WAPFORUM//DTD WML 1.1//EN' 'http://www.wapforum.org/DTD/wml_1.1.xml'><wml><head>
<meta http-equiv=""Cache-Control"" content=""max-age=0"" forua=""true""/>
</head>
<card>
<p>
<b><big>{0}</big></b><br/>
<b><i>{1}</i></b><br/>
";
        internal const string WmlErrorEndTemplate = @"</p>
</card>
</wml>
";
 
        internal const string XhtmlErrorBeginTemplate = @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE html PUBLIC ""-//WAPFORUM//DTD XHTML Mobile 1.0//EN"" ""http://www.wapforum.org/DTD/xhtml-mobile10.dtd"">
<html xmlns=""http://www.w3.org/1999/xhtml"">
<head>
<title></title>
</head>
<body>
<form>
<div>
<span style=""color:Red;font-size:Large;font-weight:bold;"">{0}</span><br/>
<span style=""color:Maroon;font-weight:bold;font-style:italic;"">{1}</span><br/>
";
        internal const string XhtmlErrorEndTemplate = @"</div>
</form>
</body>
</html>";
        internal const string Break = "<br/>\r\n";
    }
}