File: UI\WebControls\LoginUtil.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="LoginUtil.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.UI.WebControls {
 
    using System;
    using System.Collections.Specialized;
    using System.Drawing;
    using System.Net.Mail;
    using System.Security.Principal;
    using System.Web.Security;
 
    // Utility methods used by the Login controls
    internal static class LoginUtil {
        private const string _userNameReplacementKey = "<%\\s*UserName\\s*%>";
        private const string _passwordReplacementKey = "<%\\s*Password\\s*%>";
        private const string _templateDesignerRegion = "0";
 
        // Will apply style to literals if provided and determines if it should be visible
        internal static void ApplyStyleToLiteral(Literal literal, string text, Style style, bool setTableCellVisible) {
            // setTableCellVisible is used when we DO NOT make the whole table cell invisible, because in some layouts
            // it must exist for things to align correctly and its uncommon that this property will be empty anyway.
 
            bool visible = false;
            if (!String.IsNullOrEmpty(text)) {
                literal.Text = text;
                if (style != null) {
                    LoginUtil.SetTableCellStyle(literal, style);
                }
                visible = true;
            }
 
            if (setTableCellVisible) {
                LoginUtil.SetTableCellVisible(literal, visible);
            }
            else {
                literal.Visible = visible;
            }
        }
 
        // These two functions are used by the LoginControls for the border table styles
        internal static void CopyBorderStyles(WebControl control, Style style) {
            if (style == null || style.IsEmpty) {
                return;
            }
 
            control.BorderStyle = style.BorderStyle;
            control.BorderColor = style.BorderColor;
            control.BorderWidth = style.BorderWidth;
            control.BackColor = style.BackColor;
            control.CssClass = style.CssClass;
        }
 
        internal static void CopyStyleToInnerControl(WebControl control, Style style) {
            if (style == null || style.IsEmpty) {
                return;
            }
 
            control.ForeColor = style.ForeColor;
            control.Font.CopyFrom(style.Font);
        }
 
        internal static Table CreateChildTable(bool convertingToTemplate) {
            if (convertingToTemplate) {
                return new Table();
            }
            return new ChildTable(2);
        }
 
        private static MailMessage CreateMailMessage(string email, string userName, string password,
                                                     MailDefinition mailDefinition, string defaultBody,
                                                     Control owner) {
            ListDictionary replacements = new ListDictionary();
 
            // Need to html encode the username and password if the body is HTML
            if (mailDefinition.IsBodyHtml) {
                userName = HttpUtility.HtmlEncode(userName);
                password = HttpUtility.HtmlEncode(password);
            }
            replacements.Add(_userNameReplacementKey, userName);
            replacements.Add(_passwordReplacementKey, password);
            
            if (String.IsNullOrEmpty(mailDefinition.BodyFileName) && defaultBody != null) {
                return mailDefinition.CreateMailMessage(email, replacements, defaultBody, owner);
            }
            else {
                return mailDefinition.CreateMailMessage(email, replacements, owner);
            }
        }
        
        internal static MembershipProvider GetProvider(string providerName) {
            MembershipProvider provider;
            if (String.IsNullOrEmpty(providerName)) {
                provider = Membership.Provider;
            }
            else {
                provider = Membership.Providers[providerName];
                if (provider == null) {
                    throw new HttpException(SR.GetString(SR.WebControl_CantFindProvider));
                }
            }
            return provider;
        }
 
        // Returns the IPrincipal of the currently logged in user.  Returns null if no user
        // is logged in, or if Page and HttpContext are not available.
        internal static IPrincipal GetUser(Control c) {
            IPrincipal user = null;
 
            Page page = c.Page;
            if (page != null) {
                user = page.User;
            }
            else {
                HttpContext context = HttpContext.Current;
                if (context != null) {
                    user = context.User;
                }
            }
 
            return user;
        }
 
        // Returns the username of the currently logged in user.  Returns null or String.Empty
        // if no user is logged in, or if Page and HttpContext are not available.
        internal static string GetUserName(Control c) {
            string userName = null;
 
            IPrincipal user = GetUser(c);
            if (user != null) {
                IIdentity identity = user.Identity;
                if (identity != null) {
                    userName = identity.Name;
                }
            }
 
            return userName;
        }
 
        internal static void SendPasswordMail(string email, string userName, string password,
                                              MailDefinition mailDefinition,
                                              string defaultSubject, string defaultBody,
                                              OnSendingMailDelegate onSendingMailDelegate,
                                              OnSendMailErrorDelegate onSendMailErrorDelegate,
                                              Control owner) {
            // If the MailAddress ctor throws an exception, raise the error event but do not
            // rethrow the exception.  We do not rethrow the exception since the email address
            // is user-entered data, and it should not cause an unhandled exception in the page.
            // If any other part of creating or sending the MailMessage throws an exception,
            // it is most likely a developer error so the exception should be rethrown.
            // (VSWhidbey 490984)
            try {
                new MailAddress(email);
            }
            catch (Exception e) {
                SendMailErrorEventArgs args = new SendMailErrorEventArgs(e);
                // SendMailErrorEventArgs.Handled should be true, to indicate that the exception
                // will not be rethrown. (VSWhidbey 529233)
                args.Handled = true;
                onSendMailErrorDelegate(args);
                return;
            }
 
            try {
                using (MailMessage message = CreateMailMessage(email, userName, password,
                                                               mailDefinition, defaultBody,
                                                               owner)) {
                    if (mailDefinition.SubjectInternal == null && defaultSubject != null) {
                        message.Subject = defaultSubject;
                    }
                    MailMessageEventArgs args = new MailMessageEventArgs(message);
                    onSendingMailDelegate(args);
                    if (args.Cancel) {
                        return;
                    }
 
                    SmtpClient smtp = new SmtpClient();
                    smtp.Send(message);
                }
            } catch (Exception e) {
                SendMailErrorEventArgs args = new SendMailErrorEventArgs(e);
                onSendMailErrorDelegate(args);
 
                // If the error wasn't handled, we rethrow
                if (!args.Handled) {
                    throw;
                }
            }
        }
 
        // Sets the style of the table cell that contains the control.
        internal static void SetTableCellStyle(Control control, Style style) {
            Control parent = control.Parent;
            if (parent != null) {
                ((TableCell) parent).ApplyStyle(style);
            }
        }
 
        // Sets the visibility of the table cell that contains the control.  The whole cell is made invisible
        // to shrink rendered size and improve rendered appearance if cell padding or spacing is set.
        internal static void SetTableCellVisible(Control control, bool visible) {
            Control parent = control.Parent;
            if (parent != null) {
                parent.Visible = visible;
            }
        }
 
        internal delegate void OnSendingMailDelegate(MailMessageEventArgs e);
        internal delegate void OnSendMailErrorDelegate(SendMailErrorEventArgs e);
 
        /// <devdoc>
        /// TableRow that only renders if any of its cells are visible.  Improves the appearance
        /// of the control by removing empty rows.  Use this class instead of changing the
        /// visibility of the table rows, since that causes problems in the designer.
        /// (VSWhidbey 81265)
        /// </devdoc>
        internal sealed class DisappearingTableRow : TableRow {
            protected internal override void Render(HtmlTextWriter writer) {
                bool rowVisible = false;
                foreach (TableCell cell in Cells) {
                    if (cell.Visible) {
                        rowVisible = true;
                        break;
                    }
                }
 
                if (rowVisible) {
                    base.Render(writer);
                }
            }
        }
 
        /// <devdoc>
        /// The base class for all containers used for individual views in the
        /// Login and PasswordRecovery controls.  Internal because used from PasswordRecovery.
        /// </devdoc>
        internal abstract class GenericContainer<ControlType> : WebControl where ControlType : WebControl, 
                                                                                               IBorderPaddingControl, 
                                                                                               IRenderOuterTableControl {
            private bool _renderDesignerRegion = false;
            private ControlType _owner;
 
            private Table _layoutTable;
            private Table _borderTable;
 
            public GenericContainer(ControlType owner) {
                _owner = owner;
            }
 
            internal int BorderPadding {
                get {
                    return _owner.BorderPadding;
                }
            }
 
            internal Table BorderTable {
                get {
                    return _borderTable;
                }
                set {
                    _borderTable = value;
                }
            }
 
            protected abstract bool ConvertingToTemplate { get; }
 
            internal Table LayoutTable {
                get {
                    return _layoutTable;
                }
                set {
                    _layoutTable = value;
                }
            }
 
            internal ControlType Owner {
                get {
                    return _owner;
                }
            }
 
            internal bool RenderDesignerRegion {
                get {
                    return DesignMode && _renderDesignerRegion;
                }
                set {
                    _renderDesignerRegion = value;
                }
            }
 
            private bool RenderOuterTable {
                get {
                    return _owner.RenderOuterTable;
                }
            }
 
            // Returns true when using the default template, and false when using a custom template.
            private bool UsingDefaultTemplate {
                get {
                    return (BorderTable != null);
                }
            }
 
            public sealed override void Focus() {
                throw new NotSupportedException(SR.GetString(SR.NoFocusSupport, this.GetType().Name));
            }
 
            private Control FindControl<RequiredType>(string id, bool required, string errorResourceKey) {
                Control control = FindControl(id);
                if (control is RequiredType) {
                    return control;
                }
                else {
                    // Do not throw exception at design-time (VSWhidbey 429452)
                    if (required && !Owner.DesignMode) {
                        throw new HttpException(SR.GetString(errorResourceKey, Owner.ID, id));
                    }
                    else {
                        return null;
                    }
                }
            }
 
            protected Control FindOptionalControl<RequiredType>(string id) {
                return FindControl<RequiredType>(id, false, null);
            }
 
            protected Control FindRequiredControl<RequiredType>(string id, string errorResourceKey) {
                return FindControl<RequiredType>(id, true, errorResourceKey);
            }
 
            protected internal virtual string ModifiedOuterTableStylePropertyName() {
                // Verify that style properties are not set (not different than their defaults).
                if (BorderPadding != 1) {
                    return "BorderPadding";
                }
                return ModifiedOuterTableBasicStylePropertyName(Owner);
            }
 
            /// <devdoc>
            ///     Renders the template contents.  The default template is rendered directly since it is already a table.
            ///     A user template is rendered inside a table with one row and one cell.
            ///     User a single-cell table instead of a <div>, since the <div> always spans the full width of its
            ///     containing element, while a <table> sets width to contents.
            /// </devdoc>
            protected internal sealed override void Render(HtmlTextWriter writer) {
                if (!RenderOuterTable) {
                    string propertyName = ModifiedOuterTableStylePropertyName();
                    if (!string.IsNullOrEmpty(propertyName)) {
                        throw new InvalidOperationException(SR.GetString(SR.IRenderOuterTableControl_CannotSetStyleWhenDisableRenderOuterTable,
                                                            propertyName, _owner.GetType().Name, _owner.ID));
                    }
                }
 
                if (UsingDefaultTemplate) {
                    // If we are converting to template, then do not apply the base attributes or ControlStyle
                    // to the BorderTable or LayoutTable.  These will be rendered around the template contents
                    // at runtime.
                    if (!ConvertingToTemplate) {
                        BorderTable.CopyBaseAttributes(this);
                        if (ControlStyleCreated) {
                            // I assume we apply the BorderStyles and Font/ForeColor separately to make the rendering
                            // work in IE Quirks mode.  If we only wanted to support Standards mode, we could probably
                            // apply all the styles to the BorderTable.  I'm not changing this for Whidbey RTM
                            // since it is high-risk.
                            LoginUtil.CopyBorderStyles(BorderTable, ControlStyle);
                            LoginUtil.CopyStyleToInnerControl(LayoutTable, ControlStyle);
                        }
                    }
 
                    // Need to set height and width even when converting to template, to force the inner
                    // LayoutTable to fill the contents of the outer control.
                    LayoutTable.Height = Height;
                    LayoutTable.Width = Width;
 
                    if (RenderOuterTable) {
                        RenderContents(writer);
                    }
                    else {
                        // just render what would have been rendered in the outer table's single cell
                        ControlCollection controlsInCell = BorderTable.Rows[0].Cells[0].Controls;
                        RenderControls(writer, controlsInCell);
                    }
                }
                else {
                    RenderContentsInUnitTable(writer);
                }
            }
 
            private void RenderContentsInUnitTable(HtmlTextWriter writer) {
                // there are two situations in which we need an outer table:
                // 1) it was explicitly asked for (RenderOuterTable property), or
                // 2) a custom template is in use and the designer needs a container to host 
                //    the editable inner region (RenderDesignerRegion property)
                
                if (!RenderOuterTable && !RenderDesignerRegion) {
                    RenderControls(writer, Controls);
                    return;
                }
 
                LayoutTable table = new LayoutTable(1, 1, Page);
 
                // Don't render out the child controls if we are using region editing, just output the region attribute
                if (RenderDesignerRegion) {
                    table[0, 0].Attributes[HtmlTextWriter.DesignerRegionAttributeName] = _templateDesignerRegion;
                }
                else {
                    foreach (Control child in Controls) {
                        table[0, 0].Controls.Add(child);
                    }
                }
 
                // don't want to copy any style attributes to the outer table if we're only rendering it to satisfy
                // the designer, otherwise the designer will show styles that won't be rendered on an actual page
                if (RenderOuterTable) {
                    string parentID = Parent.ID;
                    if ((parentID != null) && (parentID.Length != 0)) {
                        table.ID = Parent.ClientID;
                    }
 
                    table.CopyBaseAttributes(this);
                    table.ApplyStyle(ControlStyle);
 
                    // This table should not add any cell padding or spacing around the template contents
                    table.CellPadding = 0;
                    table.CellSpacing = 0;
                }
 
                table.RenderControl(writer);
            }
 
            private static void RenderControls(HtmlTextWriter writer, ControlCollection controls) {
                foreach (Control child in controls) {
                    child.RenderControl(writer);
                }
            }
 
            // Throws an exception if a control with the specified id and type is found within
            // the container.  Does not throw exception at design-time.
            protected void VerifyControlNotPresent<RequiredType>(string id, string errorResourceKey) {
                Control control = FindOptionalControl<RequiredType>(id);
                if (control != null && !Owner.DesignMode) {
                    throw new HttpException(SR.GetString(errorResourceKey, Owner.ID, id));
                }
            }
        }
 
        internal static string ModifiedOuterTableBasicStylePropertyName(WebControl control) {
            // Verify that basic style properties are not set (not different than their defaults).
            if (control.BackColor != Color.Empty) {
                return "BackColor";
            }
            if (control.BorderColor != Color.Empty) {
                return "BorderColor";
            }
            if (control.BorderWidth != Unit.Empty) {
                return "BorderWidth";
            }
            if (control.BorderStyle != BorderStyle.NotSet) {
                return "BorderStyle";
            }
            if (!String.IsNullOrEmpty(control.CssClass)) {
                return "CssClass";
            }
            if (control.ForeColor != Color.Empty) {
                return "ForeColor";
            }
            if (control.Height != Unit.Empty) {
                return "Height";
            }
            if (control.Width != Unit.Empty) {
                return "Width";
            }
            return String.Empty;
        }
    }
}