[asp.net] Fix for bug #653192. making MasterPageFile handle relative paths
[mono-project.git] / mcs / class / System.Web / System.Web.UI / PageParser.cs
blob82769d3b2b91a49f5fd5f05869c6745275375bc1
1 //
2 // System.Web.UI.PageParser
3 //
4 // Authors:
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
8 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
31 using System.Collections.Specialized;
32 using System.Globalization;
33 using System.Security.Permissions;
34 using System.Text;
35 using System.Web.Compilation;
36 using System.Web.Configuration;
37 using System.Web.Hosting;
38 using System.Web.Util;
39 using System.IO;
41 namespace System.Web.UI
43 // CAS - no InheritanceDemand here as the class is sealed
44 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 public sealed class PageParser : TemplateControlParser
47 #if NET_4_0
48 static Type defaultPageBaseType;
49 static Type defaultApplicationBaseType;
50 static Type defaultPageParserFilterType;
51 static Type defaultUserControlBaseType;
52 static bool enableLongStringsAsResources = true;
53 #endif
54 PagesEnableSessionState enableSessionState = PagesEnableSessionState.True;
55 bool enableViewStateMac;
56 bool enableViewStateMacSet;
57 bool smartNavigation;
58 bool haveTrace;
59 bool trace;
60 bool notBuffer;
61 TraceMode tracemode = TraceMode.Default;
62 string contentType;
63 MainDirectiveAttribute <int> codepage;
64 MainDirectiveAttribute <string> responseEncoding;
65 MainDirectiveAttribute <int> lcid;
66 MainDirectiveAttribute <string> clientTarget;
67 MainDirectiveAttribute <string> masterPage;
68 MainDirectiveAttribute <string> title;
69 MainDirectiveAttribute <string> theme;
70 #if NET_4_0
71 MainDirectiveAttribute <string> metaDescription;
72 MainDirectiveAttribute <string> metaKeywords;
73 #endif
74 string culture;
75 string uiculture;
76 string errorPage;
77 bool validateRequest;
78 bool async;
79 int asyncTimeout = -1;
80 Type masterType;
81 string masterVirtualPath;
82 string styleSheetTheme;
83 bool enable_event_validation;
84 bool maintainScrollPositionOnPostBack;
85 int maxPageStateFieldLength = -1;
86 Type previousPageType;
87 string previousPageVirtualPath;
88 #if NET_4_0
89 public static bool EnableLongStringsAsResources {
90 get { return enableLongStringsAsResources; }
91 set {
92 BuildManager.AssertPreStartMethodsRunning ();
93 enableLongStringsAsResources = value;
97 public static Type DefaultPageBaseType {
98 get { return defaultPageBaseType; }
99 set {
100 BuildManager.AssertPreStartMethodsRunning ();
101 if (value != null && !typeof (Page).IsAssignableFrom (value))
102 throw new ArgumentException (String.Format ("The value assigned to property '{0}' is invalid.", "DefaultPageBaseType"));
104 defaultPageBaseType = value;
108 public static Type DefaultApplicationBaseType {
109 get { return defaultApplicationBaseType; }
110 set {
111 BuildManager.AssertPreStartMethodsRunning ();
112 if (value != null && !typeof (HttpApplication).IsAssignableFrom (value))
113 throw new ArgumentException (String.Format ("The value assigned to property '{0}' is invalid.", "DefaultApplicationBaseType"));
114 defaultApplicationBaseType = value;
118 public static Type DefaultPageParserFilterType {
119 get { return defaultPageParserFilterType; }
120 set {
121 BuildManager.AssertPreStartMethodsRunning ();
122 if (value != null && !typeof (PageParserFilter).IsAssignableFrom (value))
123 throw new ArgumentException (String.Format ("The value assigned to property '{0}' is invalid.", "DefaultPageParserFilterType"));
124 defaultPageParserFilterType = value;
128 public static Type DefaultUserControlBaseType {
129 get { return defaultUserControlBaseType; }
130 set {
131 if (value != null && !typeof (UserControl).IsAssignableFrom (value))
132 throw new ArgumentException (String.Format ("The value assigned to property '{0}' is invalid.", "DefaultUserControlBaseType"));
133 BuildManager.AssertPreStartMethodsRunning ();
134 defaultUserControlBaseType = value;
137 #endif
138 public PageParser ()
140 LoadConfigDefaults ();
143 internal PageParser (string virtualPath, string inputFile, HttpContext context)
145 this.VirtualPath = new VirtualPath (virtualPath);
146 Context = context;
147 BaseVirtualDir = VirtualPathUtility.GetDirectory (virtualPath, false);
148 InputFile = inputFile;
149 SetBaseType (null);
150 AddApplicationAssembly ();
151 LoadConfigDefaults ();
154 internal PageParser (VirtualPath virtualPath, TextReader reader, HttpContext context)
155 : this (virtualPath, null, reader, context)
159 internal PageParser (VirtualPath virtualPath, string inputFile, TextReader reader, HttpContext context)
161 this.VirtualPath = virtualPath;
162 Context = context;
163 BaseVirtualDir = virtualPath.DirectoryNoNormalize;
164 Reader = reader;
165 if (String.IsNullOrEmpty (inputFile))
166 InputFile = virtualPath.PhysicalPath;
167 else
168 InputFile = inputFile;
169 SetBaseType (null);
170 AddApplicationAssembly ();
171 LoadConfigDefaults ();
174 internal override void LoadConfigDefaults ()
176 base.LoadConfigDefaults ();
177 PagesSection ps = PagesConfig;
179 notBuffer = !ps.Buffer;
180 enableSessionState = ps.EnableSessionState;
181 enableViewStateMac = ps.EnableViewStateMac;
182 smartNavigation = ps.SmartNavigation;
183 validateRequest = ps.ValidateRequest;
185 string value = ps.MasterPageFile;
186 if (value.Length > 0)
187 masterPage = new MainDirectiveAttribute <string> (value, true);
189 enable_event_validation = ps.EnableEventValidation;
190 maxPageStateFieldLength = ps.MaxPageStateFieldLength;
191 value = ps.Theme;
192 if (value.Length > 0)
193 theme = new MainDirectiveAttribute <string> (value, true);
195 styleSheetTheme = ps.StyleSheetTheme;
196 if (styleSheetTheme.Length == 0)
197 styleSheetTheme = null;
198 maintainScrollPositionOnPostBack = ps.MaintainScrollPositionOnPostBack;
201 public static IHttpHandler GetCompiledPageInstance (string virtualPath, string inputFile, HttpContext context)
203 bool isFake = false;
205 if (!String.IsNullOrEmpty (inputFile))
206 isFake = !inputFile.StartsWith (HttpRuntime.AppDomainAppPath);
208 return BuildManager.CreateInstanceFromVirtualPath (new VirtualPath (virtualPath, inputFile, isFake), typeof (IHttpHandler)) as IHttpHandler;
211 internal override void ProcessMainAttributes (IDictionary atts)
213 // note: the 'enableSessionState' configuration property is
214 // processed in a case-sensitive manner while the page-level
215 // attribute is processed case-insensitive
216 string enabless = GetString (atts, "EnableSessionState", null);
217 if (enabless != null) {
218 if (String.Compare (enabless, "readonly", true, Helpers.InvariantCulture) == 0)
219 enableSessionState = PagesEnableSessionState.ReadOnly;
220 else if (String.Compare (enabless, "true", true, Helpers.InvariantCulture) == 0)
221 enableSessionState = PagesEnableSessionState.True;
222 else if (String.Compare (enabless, "false", true, Helpers.InvariantCulture) == 0)
223 enableSessionState = PagesEnableSessionState.False;
224 else
225 ThrowParseException ("Invalid value for enableSessionState: " + enabless);
228 string value = GetString (atts, "CodePage", null);
229 if (value != null) {
230 if (responseEncoding != null)
231 ThrowParseException ("CodePage and ResponseEncoding are mutually exclusive.");
233 if (!BaseParser.IsExpression (value)) {
234 int cpval = -1;
236 try {
237 cpval = (int) UInt32.Parse (value);
238 } catch {
239 ThrowParseException ("Invalid value for CodePage: " + value);
242 try {
243 Encoding.GetEncoding (cpval);
244 } catch {
245 ThrowParseException ("Unsupported codepage: " + value);
247 codepage = new MainDirectiveAttribute <int> (cpval, true);
248 } else
249 codepage = new MainDirectiveAttribute <int> (value);
252 value = GetString (atts, "ResponseEncoding", null);
253 if (value != null) {
254 if (codepage != null)
255 ThrowParseException ("CodePage and ResponseEncoding are mutually exclusive.");
257 if (!BaseParser.IsExpression (value)) {
258 try {
259 Encoding.GetEncoding (value);
260 } catch {
261 ThrowParseException ("Unsupported encoding: " + value);
263 responseEncoding = new MainDirectiveAttribute <string> (value, true);
264 } else
265 responseEncoding = new MainDirectiveAttribute <string> (value);
268 contentType = GetString (atts, "ContentType", null);
270 value = GetString (atts, "LCID", null);
271 if (value != null) {
272 if (!BaseParser.IsExpression (value)) {
273 int parsedLcid = -1;
274 try {
275 parsedLcid = (int) UInt32.Parse (value);
276 } catch {
277 ThrowParseException ("Invalid value for LCID: " + value);
280 CultureInfo ci = null;
281 try {
282 ci = new CultureInfo (parsedLcid);
283 } catch {
284 ThrowParseException ("Unsupported LCID: " + value);
287 if (ci.IsNeutralCulture) {
288 string suggestedCulture = SuggestCulture (ci.Name);
289 string fmt = "LCID attribute must be set to a non-neutral Culture.";
290 if (suggestedCulture != null) {
291 ThrowParseException (fmt + " Please try one of these: " +
292 suggestedCulture);
293 } else {
294 ThrowParseException (fmt);
297 lcid = new MainDirectiveAttribute <int> (parsedLcid, true);
298 } else
299 lcid = new MainDirectiveAttribute <int> (value);
302 culture = GetString (atts, "Culture", null);
303 if (culture != null) {
304 if (lcid != null)
305 ThrowParseException ("Culture and LCID are mutually exclusive.");
307 CultureInfo ci = null;
308 try {
309 if (!culture.StartsWith ("auto"))
310 ci = new CultureInfo (culture);
311 } catch {
312 ThrowParseException ("Unsupported Culture: " + culture);
315 if (ci != null && ci.IsNeutralCulture) {
316 string suggestedCulture = SuggestCulture (culture);
317 string fmt = "Culture attribute must be set to a non-neutral Culture.";
318 if (suggestedCulture != null)
319 ThrowParseException (fmt +
320 " Please try one of these: " + suggestedCulture);
321 else
322 ThrowParseException (fmt);
326 uiculture = GetString (atts, "UICulture", null);
327 if (uiculture != null) {
328 CultureInfo ci = null;
329 try {
330 if (!uiculture.StartsWith ("auto"))
331 ci = new CultureInfo (uiculture);
332 } catch {
333 ThrowParseException ("Unsupported Culture: " + uiculture);
336 if (ci != null && ci.IsNeutralCulture) {
337 string suggestedCulture = SuggestCulture (uiculture);
338 string fmt = "UICulture attribute must be set to a non-neutral Culture.";
339 if (suggestedCulture != null)
340 ThrowParseException (fmt +
341 " Please try one of these: " + suggestedCulture);
342 else
343 ThrowParseException (fmt);
347 string tracestr = GetString (atts, "Trace", null);
348 if (tracestr != null) {
349 haveTrace = true;
350 atts ["Trace"] = tracestr;
351 trace = GetBool (atts, "Trace", false);
354 string tracemodes = GetString (atts, "TraceMode", null);
355 if (tracemodes != null) {
356 bool valid = true;
357 try {
358 tracemode = (TraceMode) Enum.Parse (typeof (TraceMode), tracemodes, false);
359 } catch {
360 valid = false;
363 if (!valid || tracemode == TraceMode.Default)
364 ThrowParseException ("The 'tracemode' attribute is case sensitive and must be " +
365 "one of the following values: SortByTime, SortByCategory.");
368 errorPage = GetString (atts, "ErrorPage", null);
369 validateRequest = GetBool (atts, "ValidateRequest", validateRequest);
370 value = GetString (atts, "ClientTarget", null);
371 if (value != null) {
372 if (!BaseParser.IsExpression (value)) {
373 value = value.Trim ();
375 ClientTargetSection sec = GetConfigSection <ClientTargetSection> ("system.web/clientTarget");
376 ClientTarget ct = null;
378 if ((ct = sec.ClientTargets [value]) == null)
379 value = value.ToLowerInvariant ();
381 if (ct == null && (ct = sec.ClientTargets [value]) == null) {
382 ThrowParseException (String.Format (
383 "ClientTarget '{0}' is an invalid alias. See the " +
384 "documentation for <clientTarget> config. section.",
385 clientTarget));
387 value = ct.UserAgent;
388 clientTarget = new MainDirectiveAttribute <string> (value, true);
389 } else
390 clientTarget = new MainDirectiveAttribute <string> (value);
393 notBuffer = !GetBool (atts, "Buffer", true);
394 async = GetBool (atts, "Async", false);
395 string asyncTimeoutVal = GetString (atts, "AsyncTimeout", null);
396 if (asyncTimeoutVal != null) {
397 try {
398 asyncTimeout = Int32.Parse (asyncTimeoutVal);
399 } catch (Exception) {
400 ThrowParseException ("AsyncTimeout must be an integer value");
404 value = GetString (atts, "MasterPageFile", masterPage != null ? masterPage.Value : null);
405 if (!String.IsNullOrEmpty (value)) {
406 if (!BaseParser.IsExpression (value)) {
407 value = System.Web.VirtualPathUtility.Combine(BaseVirtualDir, value);
408 if (!HostingEnvironment.VirtualPathProvider.FileExists (value))
409 ThrowParseFileNotFound (value);
410 AddDependency (value);
411 masterPage = new MainDirectiveAttribute <string> (value, true);
412 } else
413 masterPage = new MainDirectiveAttribute <string> (value);
416 value = GetString(atts, "Title", null);
417 if (value != null) {
418 if (!BaseParser.IsExpression (value))
419 title = new MainDirectiveAttribute <string> (value, true);
420 else
421 title = new MainDirectiveAttribute <string> (value);
424 value = GetString (atts, "Theme", theme != null ? theme.Value : null);
425 if (value != null) {
426 if (!BaseParser.IsExpression (value))
427 theme = new MainDirectiveAttribute <string> (value, true);
428 else
429 theme = new MainDirectiveAttribute <string> (value);
432 styleSheetTheme = GetString (atts, "StyleSheetTheme", styleSheetTheme);
433 enable_event_validation = GetBool (atts, "EnableEventValidation", enable_event_validation);
434 maintainScrollPositionOnPostBack = GetBool (atts, "MaintainScrollPositionOnPostBack", maintainScrollPositionOnPostBack);
436 if (atts.Contains ("EnableViewStateMac")) {
437 enableViewStateMac = GetBool (atts, "EnableViewStateMac", enableViewStateMac);
438 enableViewStateMacSet = true;
440 #if NET_4_0
441 value = GetString (atts, "MetaDescription", null);
442 if (value != null) {
443 if (!BaseParser.IsExpression (value))
444 metaDescription = new MainDirectiveAttribute <string> (value, true);
445 else
446 metaDescription = new MainDirectiveAttribute <string> (value);
449 value = GetString (atts, "MetaKeywords", null);
450 if (value != null) {
451 if (!BaseParser.IsExpression (value))
452 metaKeywords = new MainDirectiveAttribute <string> (value, true);
453 else
454 metaKeywords = new MainDirectiveAttribute <string> (value);
456 #endif
457 // Ignored by now
458 GetString (atts, "SmartNavigation", null);
460 base.ProcessMainAttributes (atts);
463 internal override void AddDirective (string directive, IDictionary atts)
465 bool isMasterType = String.Compare ("MasterType", directive, StringComparison.OrdinalIgnoreCase) == 0;
466 bool isPreviousPageType = isMasterType ? false : String.Compare ("PreviousPageType", directive,
467 StringComparison.OrdinalIgnoreCase) == 0;
469 string typeName = null;
470 string virtualPath = null;
471 Type type = null;
473 if (isMasterType || isPreviousPageType) {
474 PageParserFilter pfilter = PageParserFilter;
475 if (pfilter != null)
476 pfilter.PreprocessDirective (directive.ToLowerInvariant (), atts);
478 typeName = GetString (atts, "TypeName", null);
479 virtualPath = GetString (atts, "VirtualPath", null);
481 if (typeName != null && virtualPath != null)
482 ThrowParseException (
483 String.Format ("The '{0}' directive must have exactly one attribute: TypeName or VirtualPath", directive));
484 if (typeName != null) {
485 type = LoadType (typeName);
486 if (type == null)
487 ThrowParseException (String.Format ("Could not load type '{0}'.", typeName));
488 if (isMasterType)
489 masterType = type;
490 else
491 previousPageType = type;
492 } else if (!String.IsNullOrEmpty (virtualPath)) {
493 if (!HostingEnvironment.VirtualPathProvider.FileExists (virtualPath))
494 ThrowParseFileNotFound (virtualPath);
496 AddDependency (virtualPath);
497 if (isMasterType)
498 masterVirtualPath = virtualPath;
499 else
500 previousPageVirtualPath = virtualPath;
501 } else
502 ThrowParseException (String.Format ("The {0} directive must have either a TypeName or a VirtualPath attribute.", directive));
504 if (type != null)
505 AddAssembly (type.Assembly, true);
506 } else
507 base.AddDirective (directive, atts);
510 static string SuggestCulture (string culture)
512 string retval = null;
513 foreach (CultureInfo ci in CultureInfo.GetCultures (CultureTypes.SpecificCultures)) {
514 if (ci.Name.StartsWith (culture))
515 retval += ci.Name + " ";
517 return retval;
520 internal Type GetCompiledPageType (string virtualPath, string inputFile, HttpContext context)
522 return BuildManager.GetCompiledType (virtualPath);
525 internal override Type CompileIntoType ()
527 AspGenerator generator = new AspGenerator (this);
528 return generator.GetCompiledType ();
531 internal bool EnableSessionState {
532 get {
533 return enableSessionState == PagesEnableSessionState.True ||
534 ReadOnlySessionState;
538 internal bool EnableViewStateMac {
539 get { return enableViewStateMac; }
542 internal bool EnableViewStateMacSet {
543 get { return enableViewStateMacSet; }
546 internal bool SmartNavigation {
547 get { return smartNavigation; }
550 internal bool ReadOnlySessionState {
551 get {
552 return enableSessionState == PagesEnableSessionState.ReadOnly;
556 internal bool HaveTrace {
557 get { return haveTrace; }
560 internal bool Trace {
561 get { return trace; }
564 internal TraceMode TraceMode {
565 get { return tracemode; }
567 #if NET_4_0
568 internal override Type DefaultBaseType {
569 get {
570 Type ret = DefaultPageBaseType;
571 if (ret == null)
572 return base.DefaultBaseType;
574 return ret;
577 #endif
578 internal override string DefaultBaseTypeName {
579 get { return PagesConfig.PageBaseType; }
582 internal override string DefaultDirectiveName {
583 get { return "page"; }
586 internal string ContentType {
587 get { return contentType; }
590 internal MainDirectiveAttribute <string> ResponseEncoding {
591 get { return responseEncoding; }
594 internal MainDirectiveAttribute <int> CodePage {
595 get { return codepage; }
598 internal MainDirectiveAttribute <int> LCID {
599 get { return lcid; }
602 internal MainDirectiveAttribute <string> ClientTarget {
603 get { return clientTarget; }
606 internal MainDirectiveAttribute <string> MasterPageFile {
607 get { return masterPage; }
610 internal MainDirectiveAttribute <string> Title {
611 get { return title; }
614 internal MainDirectiveAttribute <string> Theme {
615 get { return theme; }
617 #if NET_4_0
618 internal MainDirectiveAttribute <string> MetaDescription {
619 get { return metaDescription; }
622 internal MainDirectiveAttribute <string> MetaKeywords {
623 get { return metaKeywords; }
625 #endif
626 internal string Culture {
627 get { return culture; }
630 internal string UICulture {
631 get { return uiculture; }
634 internal string ErrorPage {
635 get { return errorPage; }
638 internal bool ValidateRequest {
639 get { return validateRequest; }
642 internal bool NotBuffer {
643 get { return notBuffer; }
646 internal bool Async {
647 get { return async; }
650 internal int AsyncTimeout {
651 get { return asyncTimeout; }
654 internal string StyleSheetTheme {
655 get { return styleSheetTheme; }
658 internal Type MasterType {
659 get {
660 if (masterType == null && !String.IsNullOrEmpty (masterVirtualPath))
661 masterType = BuildManager.GetCompiledType (masterVirtualPath);
663 return masterType;
667 internal bool EnableEventValidation {
668 get { return enable_event_validation; }
671 internal bool MaintainScrollPositionOnPostBack {
672 get { return maintainScrollPositionOnPostBack; }
675 internal int MaxPageStateFieldLength {
676 get { return maxPageStateFieldLength; }
679 internal Type PreviousPageType {
680 get {
681 if (previousPageType == null && !String.IsNullOrEmpty (previousPageVirtualPath)) {
682 string mappedPath = MapPath (previousPageVirtualPath);
683 previousPageType = GetCompiledPageType (previousPageVirtualPath, mappedPath, HttpContext.Current);
686 return previousPageType;