// // System.Web.UI.SimpleWebHandlerParser // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) // // (C) 2002,2003 Ximian, Inc (http://www.ximian.com) // Copyright (C) 2005 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.CodeDom.Compiler; using System.Collections; using System.IO; using System.Reflection; using System.Security.Permissions; using System.Text; using System.Web.Compilation; using System.Web.Configuration; using System.Web.Util; namespace System.Web.UI { // CAS [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public abstract class SimpleWebHandlerParser { HttpContext context; string vPath; string physPath; string className; bool debug; string language; string program; bool gotDefault; ArrayList assemblies; ArrayList dependencies; Hashtable anames; string privateBinPath; string baseDir; string baseVDir; #if !NET_2_0 CompilationConfiguration compilationConfig; #else TextReader reader; #endif int appAssemblyIndex = -1; Type cachedType; protected SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath) { cachedType = CachingCompiler.GetTypeFromCache (physicalPath); if (cachedType != null) return; // We don't need anything else. this.context = context; this.vPath = virtualPath; this.physPath = physicalPath; AddDependency (physicalPath); assemblies = new ArrayList (); string location = Context.ApplicationInstance.AssemblyLocation; if (location != typeof (TemplateParser).Assembly.Location) appAssemblyIndex = assemblies.Add (location); #if NET_2_0 bool addAssembliesInBin = false; foreach (AssemblyInfo info in CompilationConfig.Assemblies) { if (info.Assembly == "*") addAssembliesInBin = true; else AddAssemblyByName (info.Assembly, null); } if (addAssembliesInBin) AddAssembliesInBin (); #else assemblies.AddRange (CompilationConfig.Assemblies); if (CompilationConfig.AssembliesInBin) AddAssembliesInBin (); #endif language = CompilationConfig.DefaultLanguage; GetDirectivesAndContent (); } #if NET_2_0 internal SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath, TextReader reader) : this (context, virtualPath, physicalPath) { this.reader = reader; } #endif protected Type GetCompiledTypeFromCache () { return cachedType; } void GetDirectivesAndContent () { string line; bool directiveFound = false; bool inDirective = false; StringBuilder directive = null; StringBuilder content = new StringBuilder (); int idxStart, idxEnd, length; using (StreamReader reader = new StreamReader (File.OpenRead (physPath))) { while ((line = reader.ReadLine ()) != null && cachedType == null) { length = line.Length; if (length == 0) { content.Append ("\n"); continue; } idxStart = line.IndexOf ("<%"); if (idxStart > -1) { idxEnd = line.IndexOf ("%>"); if (idxStart > 0) content.Append (line.Substring (0, idxStart)); if (directive == null) directive = new StringBuilder (); else directive.Length = 0; if (idxEnd > -1) { directiveFound = true; inDirective = false; directive.Append (line.Substring (idxStart, idxEnd - idxStart + 2)); if (idxEnd < length - 2) content.Append (line.Substring (idxEnd + 2, length - idxEnd - 2)); } else { inDirective = true; directiveFound = false; directive.Append (line.Substring (idxStart)); continue; } } if (inDirective) { int idx = line.IndexOf ("%>"); if (idx > -1) { directive.Append (line.Substring (0, idx + 2)); if (idx < length) content.Append (line.Substring (idx + 2) + "\n"); inDirective = false; directiveFound = true; } else { directive.Append (line); continue; } } if (directiveFound) { ParseDirective (directive.ToString ()); directiveFound = false; if (gotDefault) { cachedType = CachingCompiler.GetTypeFromCache (physPath); if (cachedType != null) break; } continue; } content.Append (line + "\n"); } directive = null; } if (!gotDefault) throw new ParseException (null, "No @" + DefaultDirectiveName + " directive found"); if (cachedType == null) this.program = content.ToString (); } void TagParsed (ILocation location, System.Web.Compilation.TagType tagtype, string tagid, TagAttributes attributes) { if (tagtype != System.Web.Compilation.TagType.Directive) throw new ParseException (location, "Unexpected tag"); if (String.Compare (tagid, DefaultDirectiveName, true) == 0) { AddDefaultDirective (location, attributes); } else if (String.Compare (tagid, "Assembly", true) == 0) { AddAssemblyDirective (location, attributes); } else { throw new ParseException (location, "Unexpected directive: " + tagid); } } void TextParsed (ILocation location, string text) { if (text.Trim () != "") throw new ParseException (location, "Text not allowed here"); } void ParseError (ILocation location, string message) { throw new ParseException (location, message); } static string GetAndRemove (Hashtable table, string key) { string o = table [key] as string; table.Remove (key); return o; } void ParseDirective (string line) { AspParser parser = new AspParser (physPath, new StringReader (line)); parser.Error += new ParseErrorHandler (ParseError); parser.TagParsed += new TagParsedHandler (TagParsed); parser.TextParsed += new TextParsedHandler (TextParsed); parser.Parse (); } internal virtual void AddDefaultDirective (ILocation location, TagAttributes attrs) { if (gotDefault) throw new ParseException (location, "duplicate " + DefaultDirectiveName + " directive"); gotDefault = true; Hashtable attributes = attrs.GetDictionary (null); className = GetAndRemove (attributes, "class"); if (className == null) throw new ParseException (null, "No Class attribute found."); string d = GetAndRemove (attributes, "debug"); if (d != null) { debug = (String.Compare (d, "true", true) == 0); if (debug == false && String.Compare (d, "false", true) != 0) throw new ParseException (null, "Invalid value for Debug attribute"); } language = GetAndRemove (attributes, "language"); if (language == null) language = CompilationConfig.DefaultLanguage; GetAndRemove (attributes, "codebehind"); if (attributes.Count > 0) throw new ParseException (location, "Unrecognized attribute in " + DefaultDirectiveName + " directive"); } internal virtual void AddAssemblyDirective (ILocation location, TagAttributes attrs) { Hashtable tbl = attrs.GetDictionary (null); string name = GetAndRemove (tbl, "Name"); string src = GetAndRemove (tbl, "Src"); if (name == null && src == null) throw new ParseException (location, "You gotta specify Src or Name"); if (name != null && src != null) throw new ParseException (location, "Src and Name cannot be used together"); if (name != null) { AddAssemblyByName (name, location); } else { GetAssemblyFromSource (src, location); } if (tbl.Count > 0) throw new ParseException (location, "Unrecognized attribute in Assembly directive"); } internal virtual void AddAssembly (Assembly assembly, bool fullPath) { if (anames == null) anames = new Hashtable (); string name = assembly.GetName ().Name; string loc = assembly.Location; if (fullPath) { if (!assemblies.Contains (loc)) { assemblies.Add (loc); } anames [name] = loc; anames [loc] = assembly; } else { if (!assemblies.Contains (name)) { assemblies.Add (name); } anames [name] = assembly; } } internal virtual Assembly AddAssemblyByName (string name, ILocation location) { if (anames == null) anames = new Hashtable (); if (anames.Contains (name)) { object o = anames [name]; if (o is string) o = anames [o]; return (Assembly) o; } Assembly assembly = LoadAssemblyFromBin (name); if (assembly != null) { AddAssembly (assembly, true); return assembly; } try { assembly = Assembly.LoadWithPartialName (name); } catch (Exception e) { throw new ParseException (location, "Assembly " + name + " not found", e); } AddAssembly (assembly, true); return assembly; } void AddAssembliesInBin () { foreach (string bindir in HttpApplication.PrivateBinPath) { if (!Directory.Exists (bindir)) continue; string [] binDlls = Directory.GetFiles (bindir, "*.dll"); foreach (string s in binDlls) { try { Assembly assembly = Assembly.LoadFrom (s); AddAssembly (assembly, true); } catch (Exception e) { throw new Exception ("Error while loading " + s, e); } } } } Assembly LoadAssemblyFromBin (string name) { Assembly assembly = null; foreach (string bindir in HttpApplication.PrivateBinPath) { if (!Directory.Exists (bindir)) continue; string [] binDlls = Directory.GetFiles (bindir, "*.dll"); foreach (string dll in binDlls) { string fn = Path.GetFileName (dll); fn = Path.ChangeExtension (fn, null); if (fn != name) continue; assembly = Assembly.LoadFrom (dll); return assembly; } } return null; } Assembly GetAssemblyFromSource (string vpath, ILocation location) { vpath = UrlUtils.Combine (BaseVirtualDir, vpath); string realPath = context.Request.MapPath (vpath); if (!File.Exists (realPath)) throw new ParseException (location, "File " + vpath + " not found"); AddDependency (realPath); CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies); if (result.NativeCompilerReturnValue != 0) { StreamReader reader = new StreamReader (realPath); throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ()); } AddAssembly (result.CompiledAssembly, true); return result.CompiledAssembly; } internal Type GetTypeFromBin (string typeName) { Type result = null; Type type = null; #if NET_2_0 IList toplevelAssemblies = BuildManager.TopLevelAssemblies; if (toplevelAssemblies != null && toplevelAssemblies.Count > 0) { foreach (Assembly asm in toplevelAssemblies) { type = asm.GetType (typeName, false); if (type != null) { if (result != null) throw new HttpException (String.Format ("Type {0} is not unique.", typeName)); } result = type; } } #endif foreach (string bindir in HttpApplication.PrivateBinPath) { if (!Directory.Exists (bindir)) continue; string [] binDlls = Directory.GetFiles (bindir, "*.dll"); foreach (string dll in binDlls) { Assembly assembly = Assembly.LoadFrom (dll); type = assembly.GetType (typeName, false); if (type != null) { if (result != null) throw new HttpException (String.Format ("Type {0} is not unique.", typeName)); result = type; } } } if (result == null) throw new HttpException (String.Format ("Type {0} not found.", typeName)); return result; } internal virtual void AddDependency (string filename) { if (dependencies == null) dependencies = new ArrayList (); if (!dependencies.Contains (filename)) dependencies.Add (filename); } // Properties protected abstract string DefaultDirectiveName { get; } internal HttpContext Context { get { return context; } } internal string VirtualPath { get { return vPath; } } internal string PhysicalPath { get { return physPath; } } internal string ClassName { get { return className; } } internal bool Debug { get { return debug; } } internal string Language { get { return language; } } internal string Program { get { return program; } } internal ArrayList Assemblies { get { if (appAssemblyIndex != -1) { object o = assemblies [appAssemblyIndex]; assemblies.RemoveAt (appAssemblyIndex); assemblies.Add (o); appAssemblyIndex = -1; } return assemblies; } } internal ArrayList Dependencies { get { return dependencies; } } internal string BaseDir { get { if (baseDir == null) baseDir = context.Request.MapPath (BaseVirtualDir); return baseDir; } } internal virtual string BaseVirtualDir { get { if (baseVDir == null) baseVDir = UrlUtils.GetDirectory (context.Request.FilePath); return baseVDir; } } #if NET_2_0 CompilationSection CompilationConfig { get { return (CompilationSection)WebConfigurationManager.GetSection ("system.web/compilation"); } } internal TextReader Reader { get { return reader; } set { reader = value; } } #else internal CompilationConfiguration CompilationConfig { get { if (compilationConfig == null) compilationConfig = CompilationConfiguration.GetInstance (context); return compilationConfig; } } #endif } }