|
//------------------------------------------------------------------------------
// <copyright file="PageParser.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* Implements the ASP.NET template parser
*
* Copyright (c) 1998 Microsoft Corporation
*/
namespace System.Web.UI {
using System.Runtime.Serialization.Formatters;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.Globalization;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Web.Hosting;
using System.Web.Caching;
using System.Web.Util;
using System.Web.Compilation;
using System.Web.Configuration;
using System.Web.Management;
using System.EnterpriseServices;
using HttpException = System.Web.HttpException;
using System.Text.RegularExpressions;
using System.Security.Permissions;
/*
* Parser for .aspx files
*/
/// <internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public sealed class PageParser : TemplateControlParser {
private int _transactionMode = 0 /*TransactionOption.Disabled*/;
internal int TransactionMode { get { return _transactionMode; } }
private TraceMode _traceMode = System.Web.TraceMode.Default;
internal TraceMode TraceMode { get { return _traceMode; } }
private TraceEnable _traceEnabled = TraceEnable.Default;
internal TraceEnable TraceEnabled { get { return _traceEnabled; } }
private int _codePage;
private string _responseEncoding;
private int _lcid;
private string _culture;
private int _mainDirectiveLineNumber = 1;
private bool _mainDirectiveMasterPageSet;
private OutputCacheLocation _outputCacheLocation;
internal bool FRequiresSessionState { get { return flags[requiresSessionState]; } }
internal bool FReadOnlySessionState { get { return flags[readOnlySessionState]; } }
private string _errorPage;
private string _styleSheetTheme;
internal String StyleSheetTheme { get { return _styleSheetTheme; } }
internal bool AspCompatMode { get { return flags[aspCompatMode]; } }
internal bool AsyncMode { get { return flags[asyncMode]; } }
internal bool ValidateRequest { get { return flags[validateRequest]; } }
private Type _previousPageType;
internal Type PreviousPageType { get { return _previousPageType; } }
private Type _masterPageType;
internal Type MasterPageType { get { return _masterPageType; } }
private string _configMasterPageFile;
public PageParser() {
flags[buffer] = Page.BufferDefault;
flags[requiresSessionState] = true;
flags[validateRequest] = true;
}
/*
* Compile an .aspx file into a Page object
*/
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
private static object s_lock = new object();
// Only allowed in full trust (ASURT 123086)
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public static IHttpHandler GetCompiledPageInstance(string virtualPath,
string inputFile, HttpContext context) {
// Canonicalize the path to avoid failure caused by the CheckSuspiciousPhysicalPath
// security check, which was not meant to apply to this scenario (plus this API requires
// full trust). VSWhidbey 541640.
if (!String.IsNullOrEmpty(inputFile)) {
inputFile = Path.GetFullPath(inputFile);
}
return GetCompiledPageInstance(VirtualPath.Create(virtualPath), inputFile, context);
}
private static IHttpHandler GetCompiledPageInstance(VirtualPath virtualPath,
string inputFile, HttpContext context) {
// This is a hacky API that only exists to support web service's
// DefaultWsdlHelpGenerator.aspx, which doesn't live under the app root.
// To make this work, we add an explicit mapping from the virtual path
// to the stream of the passed in file
// Make it relative to the current request if necessary
if (context != null)
virtualPath = context.Request.FilePathObject.Combine(virtualPath);
object virtualPathToFileMappingState = null;
try {
try {
// If there is a physical path, we need to connect the virtual path to it, so that
// the build system will use the right input file for the virtual path.
if (inputFile != null) {
virtualPathToFileMappingState = HostingEnvironment.AddVirtualPathToFileMapping(
virtualPath, inputFile);
}
BuildResultCompiledType result = (BuildResultCompiledType)BuildManager.GetVPathBuildResult(
context, virtualPath, false /*noBuild*/, true /*allowCrossApp*/, true /*allowBuildInPrecompile*/);
return (IHttpHandler)HttpRuntime.CreatePublicInstance(result.ResultType);
}
finally {
if (virtualPathToFileMappingState != null)
HostingEnvironment.ClearVirtualPathToFileMapping(virtualPathToFileMappingState);
}
}
catch {
throw;
}
}
internal override Type DefaultBaseType { get { return typeof(System.Web.UI.Page); } }
internal override Type DefaultFileLevelBuilderType {
get {
return typeof(FileLevelPageControlBuilder);
}
}
internal override RootBuilder CreateDefaultFileLevelBuilder() {
return new FileLevelPageControlBuilder();
}
private void EnsureMasterPageFileFromConfigApplied() {
// Skip if it's already applied.
if (_mainDirectiveMasterPageSet) {
return;
}
// If the masterPageFile is defined in the config
if (_configMasterPageFile != null) {
// Readjust the lineNumber to the location of maindirective
int prevLineNumber = _lineNumber;
_lineNumber = _mainDirectiveLineNumber;
try {
if (_configMasterPageFile.Length > 0) {
Type type = GetReferencedType(_configMasterPageFile);
// Make sure it has the correct base type
if (!typeof(MasterPage).IsAssignableFrom(type)) {
ProcessError(SR.GetString(SR.Invalid_master_base, _configMasterPageFile));
}
}
if (((FileLevelPageControlBuilder)RootBuilder).ContentBuilderEntries != null) {
RootBuilder.SetControlType(BaseType);
RootBuilder.PreprocessAttribute(String.Empty /*filter*/, "MasterPageFile", _configMasterPageFile, true /*mainDirectiveMode*/);
}
}
finally {
_lineNumber = prevLineNumber;
}
}
_mainDirectiveMasterPageSet = true;
}
internal override void HandlePostParse() {
base.HandlePostParse();
EnsureMasterPageFileFromConfigApplied();
}
// Get default settings from config
internal override void ProcessConfigSettings() {
base.ProcessConfigSettings();
if (PagesConfig != null) {
// Check config for various attributes, and if they have non-default values,
// set them in _mainDirectiveConfigSettings.
if (PagesConfig.Buffer != Page.BufferDefault)
_mainDirectiveConfigSettings["buffer"] = Util.GetStringFromBool(PagesConfig.Buffer);
if (PagesConfig.EnableViewStateMac != Page.EnableViewStateMacDefault)
_mainDirectiveConfigSettings["enableviewstatemac"] = Util.GetStringFromBool(PagesConfig.EnableViewStateMac);
if (PagesConfig.EnableEventValidation != Page.EnableEventValidationDefault)
_mainDirectiveConfigSettings["enableEventValidation"] = Util.GetStringFromBool(PagesConfig.EnableEventValidation);
if (PagesConfig.SmartNavigation != Page.SmartNavigationDefault)
_mainDirectiveConfigSettings["smartnavigation"] = Util.GetStringFromBool(PagesConfig.SmartNavigation);
if (PagesConfig.ThemeInternal != null && PagesConfig.Theme.Length != 0)
_mainDirectiveConfigSettings["theme"] = PagesConfig.Theme;
if (PagesConfig.StyleSheetThemeInternal != null && PagesConfig.StyleSheetThemeInternal.Length != 0)
_mainDirectiveConfigSettings["stylesheettheme"] = PagesConfig.StyleSheetThemeInternal;
if (PagesConfig.MasterPageFileInternal != null && PagesConfig.MasterPageFileInternal.Length != 0) {
_configMasterPageFile = PagesConfig.MasterPageFileInternal;
}
if (PagesConfig.ViewStateEncryptionMode != Page.EncryptionModeDefault) {
_mainDirectiveConfigSettings["viewStateEncryptionMode"] = Enum.Format(typeof(ViewStateEncryptionMode), PagesConfig.ViewStateEncryptionMode, "G");
}
if (PagesConfig.MaintainScrollPositionOnPostBack != Page.MaintainScrollPositionOnPostBackDefault) {
_mainDirectiveConfigSettings["maintainScrollPositionOnPostBack"] = Util.GetStringFromBool(PagesConfig.MaintainScrollPositionOnPostBack);
}
if (PagesConfig.MaxPageStateFieldLength != Page.DefaultMaxPageStateFieldLength) {
_mainDirectiveConfigSettings["maxPageStateFieldLength"] = PagesConfig.MaxPageStateFieldLength;
}
flags[requiresSessionState] = ((PagesConfig.EnableSessionState == PagesEnableSessionState.True) || (PagesConfig.EnableSessionState == PagesEnableSessionState.ReadOnly));
flags[readOnlySessionState] = (PagesConfig.EnableSessionState == PagesEnableSessionState.ReadOnly);
flags[validateRequest] = PagesConfig.ValidateRequest;
flags[aspCompatMode] = HttpRuntime.ApartmentThreading;
}
ApplyBaseType();
}
private void ApplyBaseType() {
if (DefaultPageBaseType != null) {
BaseType = DefaultPageBaseType;
}
else if (PagesConfig != null && PagesConfig.PageBaseTypeInternal != null) {
BaseType = PagesConfig.PageBaseTypeInternal;
}
}
internal override void ProcessDirective(string directiveName, IDictionary directive) {
if (StringUtil.EqualsIgnoreCase(directiveName, "previousPageType")) {
if (_previousPageType != null) {
ProcessError(SR.GetString(SR.Only_one_directive_allowed, directiveName));
return;
}
_previousPageType = GetDirectiveType(directive, directiveName);
Util.CheckAssignableType(typeof(Page), _previousPageType);
}
else if (StringUtil.EqualsIgnoreCase(directiveName, "masterType")) {
if (_masterPageType != null) {
ProcessError(SR.GetString(SR.Only_one_directive_allowed, directiveName));
return;
}
_masterPageType = GetDirectiveType(directive, directiveName);
Util.CheckAssignableType(typeof(MasterPage), _masterPageType);
}
else {
base.ProcessDirective(directiveName, directive);
}
}
// Override to get the location of maindirective.
internal override void ProcessMainDirective(IDictionary mainDirective) {
// Remember the location of the main directive.
_mainDirectiveLineNumber = _lineNumber;
base.ProcessMainDirective(mainDirective);
}
internal override bool ProcessMainDirectiveAttribute(string deviceName, string name,
string value, IDictionary parseData) {
switch (name) {
case "errorpage":
_errorPage = Util.GetNonEmptyAttribute(name, value);
// Return false to let the generic attribute processing continue
return false;
case "contenttype":
// Check validity
Util.GetNonEmptyAttribute(name, value);
// Return false to let the generic attribute processing continue
return false;
case "theme":
if (IsExpressionBuilderValue(value)) {
return false;
}
// Check validity
Util.CheckThemeAttribute(value);
// Return false to let the generic attribute processing continue
return false;
case "stylesheettheme":
// Make sure no device filter or expression builder was specified
ValidateBuiltInAttribute(deviceName, name, value);
// Check validity
Util.CheckThemeAttribute(value);
_styleSheetTheme = value;
return true;
case "enablesessionstate":
flags[requiresSessionState] = true;
flags[readOnlySessionState] = false;
if (Util.IsFalseString(value)) {
flags[requiresSessionState] = false;
}
else if (StringUtil.EqualsIgnoreCase(value, "readonly")) {
flags[readOnlySessionState] = true;
}
else if (!Util.IsTrueString(value)) {
ProcessError(SR.GetString(SR.Enablesessionstate_must_be_true_false_or_readonly));
}
if (flags[requiresSessionState]) {
// Session state is only available for compiled pages
OnFoundAttributeRequiringCompilation(name);
}
break;
case "culture":
_culture = Util.GetNonEmptyAttribute(name, value);
// Setting culture requires medium permission
if (!HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "culture"));
}
//do not verify at parse time if potentially using browser AutoDetect
if(StringUtil.EqualsIgnoreCase(value, HttpApplication.AutoCulture)) {
return false;
}
// Create a CultureInfo just to verify validity
CultureInfo cultureInfo;
try {
if(StringUtil.StringStartsWithIgnoreCase(value, HttpApplication.AutoCulture)) {
//safe to trim leading "auto:", string used elsewhere for null check
_culture = _culture.Substring(5);
}
cultureInfo = HttpServerUtility.CreateReadOnlyCultureInfo(_culture);
}
catch {
ProcessError(SR.GetString(SR.Invalid_attribute_value, _culture, "culture"));
return false;
}
// Don't allow neutral cultures (ASURT 77930)
if (cultureInfo.IsNeutralCulture) {
ProcessError(SR.GetString(SR.Invalid_culture_attribute,
Util.GetSpecificCulturesFormattedList(cultureInfo)));
}
// Return false to let the generic attribute processing continue
return false;
case "lcid":
// Skip validity check for expression builder (e.g. <%$ ... %>)
if (IsExpressionBuilderValue(value)) return false;
_lcid = Util.GetNonNegativeIntegerAttribute(name, value);
// Create a CultureInfo just to verify validity
try {
HttpServerUtility.CreateReadOnlyCultureInfo(_lcid);
}
catch {
ProcessError(SR.GetString(SR.Invalid_attribute_value,
_lcid.ToString(CultureInfo.InvariantCulture), "lcid"));
}
// Return false to let the generic attribute processing continue
return false;
case "uiculture":
// Check validity
Util.GetNonEmptyAttribute(name, value);
// Return false to let the generic attribute processing continue
return false;
case "responseencoding":
// Skip validity check for expression builder (e.g. <%$ ... %>)
if (IsExpressionBuilderValue(value)) return false;
_responseEncoding = Util.GetNonEmptyAttribute(name, value);
// Call Encoding.GetEncoding just to verify validity
Encoding.GetEncoding(_responseEncoding);
// Return false to let the generic attribute processing continue
return false;
case "codepage":
// Skip validity check for expression builder (e.g. <%$ ... %>)
if (IsExpressionBuilderValue(value)) return false;
_codePage = Util.GetNonNegativeIntegerAttribute(name, value);
// Call Encoding.GetEncoding just to verify validity
Encoding.GetEncoding(_codePage);
// Return false to let the generic attribute processing continue
return false;
case "transaction":
// This only makes sense for compiled pages
OnFoundAttributeRequiringCompilation(name);
ParseTransactionAttribute(name, value);
break;
case "aspcompat":
// This only makes sense for compiled pages
OnFoundAttributeRequiringCompilation(name);
flags[aspCompatMode] = Util.GetBooleanAttribute(name, value);
// Only allow the use of aspcompat when we have UnmanagedCode access (ASURT 76694)
if (flags[aspCompatMode] && !HttpRuntime.HasUnmanagedPermission()) {
throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "AspCompat"));
}
break;
case "async":
// This only makes sense for compiled pages
OnFoundAttributeRequiringCompilation(name);
flags[asyncMode] = Util.GetBooleanAttribute(name, value);
// Async requires Medium trust
if (!HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "async"));
}
break;
case "tracemode":
// We use TraceModeInternal instead of TraceMode to disallow the 'default' value (ASURT 75783)
object tmpObj = Util.GetEnumAttribute(name, value, typeof(TraceModeInternal));
_traceMode = (TraceMode) tmpObj;
break;
case "trace":
bool traceEnabled = Util.GetBooleanAttribute(name, value);
if (traceEnabled)
_traceEnabled = TraceEnable.Enable;
else
_traceEnabled = TraceEnable.Disable;
break;
case "smartnavigation":
// Make sure no device filter or expression builder was specified, since it doesn't make much
// sense for smartnav (which only works on IE5.5+) (VSWhidbey 85876)
ValidateBuiltInAttribute(deviceName, name, value);
// Ignore it if it has default value. Otherwise, let the generic
// attribute processing continue
bool smartNavigation = Util.GetBooleanAttribute(name, value);
return (smartNavigation == Page.SmartNavigationDefault);
case "maintainscrollpositiononpostback":
bool maintainScrollPosition = Util.GetBooleanAttribute(name, value);
return (maintainScrollPosition == Page.MaintainScrollPositionOnPostBackDefault);
case "validaterequest":
flags[validateRequest] = Util.GetBooleanAttribute(name, value);
break;
case "clienttarget":
// Skip validity check for expression builder (e.g. <%$ ... %>)
if (IsExpressionBuilderValue(value)) return false;
// Check validity
HttpCapabilitiesDefaultProvider.GetUserAgentFromClientTarget(CurrentVirtualPath, value);
// Return false to let the generic attribute processing continue
return false;
case "masterpagefile":
// Skip validity check for expression builder (e.g. <%$ ... %>)
if (IsExpressionBuilderValue(value)) return false;
if (value.Length > 0) {
// Add dependency on the Type by calling this method
Type type = GetReferencedType(value);
// Make sure it has the correct base type
if (!typeof(MasterPage).IsAssignableFrom(type)) {
ProcessError(SR.GetString(SR.Invalid_master_base, value));
}
if (deviceName.Length > 0) {
// Make sure the masterPageFile definition from config
// is applied before filtered masterPageFile attributes.
EnsureMasterPageFileFromConfigApplied();
}
}
//VSWhidbey 479064 Remember the masterPageFile had been set even if it's empty string
_mainDirectiveMasterPageSet = true;
// Return false to let the generic attribute processing continue
return false;
default:
// We didn't handle the attribute. Try the base class
return base.ProcessMainDirectiveAttribute(deviceName, name, value, parseData);
}
// The attribute was handled
// Make sure no device filter or resource expression was specified
ValidateBuiltInAttribute(deviceName, name, value);
return true;
}
internal override void ProcessUnknownMainDirectiveAttribute(string filter, string attribName, string value) {
// asynctimeout is in seconds while the corresponding public page property is a timespan
// this requires a patch-up
if (attribName == "asynctimeout") {
int timeoutInSeconds = Util.GetNonNegativeIntegerAttribute(attribName, value);
value = (new TimeSpan(0, 0, timeoutInSeconds)).ToString();
}
base.ProcessUnknownMainDirectiveAttribute(filter, attribName, value);
}
internal override void PostProcessMainDirectiveAttributes(IDictionary parseData) {
// Can't have an error page if buffering is off
if (!flags[buffer] && _errorPage != null) {
ProcessError(SR.GetString(SR.Error_page_not_supported_when_buffering_off));
return;
}
if (_culture != null && _lcid > 0) {
ProcessError(SR.GetString(SR.Attributes_mutually_exclusive, "Culture", "LCID"));
return;
}
if (_responseEncoding != null && _codePage > 0) {
ProcessError(SR.GetString(SR.Attributes_mutually_exclusive, "ResponseEncoding", "CodePage"));
return;
}
// async can't be combined with aspcompat
if (AsyncMode && AspCompatMode) {
ProcessError(SR.GetString(SR.Async_and_aspcompat));
return;
}
// async can't be combined with transactions
if (AsyncMode && _transactionMode != 0) {
ProcessError(SR.GetString(SR.Async_and_transaction));
return;
}
// Let the base class do its post processing
base.PostProcessMainDirectiveAttributes(parseData);
}
private enum TraceModeInternal {
SortByTime = 0,
SortByCategory = 1
}
// This must be in its own method to avoid jitting System.EnterpriseServices.dll
// when it is not needed (ASURT 71868)
private void ParseTransactionAttribute(string name, string value) {
object tmpObj = Util.GetEnumAttribute(name, value, typeof(TransactionOption));
if (tmpObj != null) {
_transactionMode = (int) tmpObj;
// Add a reference to the transaction assembly only if needed
if (_transactionMode != 0 /*TransactionOption.Disabled*/) {
if (!HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "transaction"));
}
AddAssemblyDependency(typeof(TransactionOption).Assembly);
}
}
}
internal const string defaultDirectiveName = "page";
internal override string DefaultDirectiveName {
get { return defaultDirectiveName; }
}
/*
* Process the contents of the <%@ OutputCache ... %> directive
*/
internal override void ProcessOutputCacheDirective(string directiveName, IDictionary directive) {
string varyByContentEncoding;
string varyByHeader;
string sqlDependency;
bool noStoreValue = false;
varyByContentEncoding = Util.GetAndRemoveNonEmptyAttribute(directive, "varybycontentencoding");
if (varyByContentEncoding != null) {
OutputCacheParameters.VaryByContentEncoding = varyByContentEncoding;
}
varyByHeader = Util.GetAndRemoveNonEmptyAttribute(directive, "varybyheader");
if (varyByHeader != null) {
OutputCacheParameters.VaryByHeader = varyByHeader;
}
object tmpObj = Util.GetAndRemoveEnumAttribute(directive, typeof(OutputCacheLocation), "location");
if (tmpObj != null) {
_outputCacheLocation = (OutputCacheLocation) tmpObj;
OutputCacheParameters.Location = _outputCacheLocation;
}
sqlDependency = Util.GetAndRemoveNonEmptyAttribute(directive, "sqldependency");
if (sqlDependency != null) {
OutputCacheParameters.SqlDependency = sqlDependency;
// Validate the sqldependency attribute
SqlCacheDependency.ValidateOutputCacheDependencyString(sqlDependency, true);
}
// Get the "no store" bool value
if (Util.GetAndRemoveBooleanAttribute(directive, "nostore", ref noStoreValue)) {
OutputCacheParameters.NoStore = noStoreValue;
}
base.ProcessOutputCacheDirective(directiveName, directive);
}
private static Type s_defaultPageBaseType;
public static Type DefaultPageBaseType {
get {
return s_defaultPageBaseType;
}
set {
if (value != null && !typeof(Page).IsAssignableFrom(value)) {
throw ExceptionUtil.PropertyInvalid("DefaultPageBaseType");
}
BuildManager.ThrowIfPreAppStartNotRunning();
s_defaultPageBaseType = value;
}
}
private static Type s_defaultUserContorlBaseType;
public static Type DefaultUserControlBaseType {
get {
return s_defaultUserContorlBaseType;
}
set {
if (value != null && !typeof(UserControl).IsAssignableFrom(value)) {
throw ExceptionUtil.PropertyInvalid("DefaultUserControlBaseType");
}
BuildManager.ThrowIfPreAppStartNotRunning();
s_defaultUserContorlBaseType = value;
}
}
private static Type s_defaultApplicationBaseType;
public static Type DefaultApplicationBaseType {
get {
return s_defaultApplicationBaseType;
}
set {
if (value != null && !typeof(HttpApplication).IsAssignableFrom(value)) {
throw ExceptionUtil.PropertyInvalid("DefaultApplicationBaseType");
}
BuildManager.ThrowIfPreAppStartNotRunning();
s_defaultApplicationBaseType = value;
}
}
private static Type s_defaultPageParserFilterType;
public static Type DefaultPageParserFilterType {
get {
return s_defaultPageParserFilterType;
}
set {
if (value != null && !typeof(PageParserFilter).IsAssignableFrom(value)) {
throw ExceptionUtil.PropertyInvalid("DefaultPageParserFilterType");
}
BuildManager.ThrowIfPreAppStartNotRunning();
s_defaultPageParserFilterType = value;
}
}
private static bool s_enableLongStringsAsResources = true;
public static bool EnableLongStringsAsResources {
get {
return s_enableLongStringsAsResources;
}
set {
BuildManager.ThrowIfPreAppStartNotRunning();
s_enableLongStringsAsResources = value;
}
}
internal override bool FDurationRequiredOnOutputCache {
get { return _outputCacheLocation != OutputCacheLocation.None; }
}
internal override bool FVaryByParamsRequiredOnOutputCache {
get { return _outputCacheLocation != OutputCacheLocation.None; }
}
internal override string UnknownOutputCacheAttributeError {
get { return SR.Attr_not_supported_in_pagedirective; }
}
}
}
|