2 // System.Web.UI.SimpleWebHandlerParser
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
8 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.CodeDom.Compiler;
31 using System.Collections;
32 using System.Globalization;
34 using System.Reflection;
35 using System.Security.Permissions;
37 using System.Web.Compilation;
38 using System.Web.Configuration;
39 using System.Web.Util;
41 namespace System.Web.UI
44 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
46 public abstract class SimpleWebHandlerParser
57 ArrayList dependencies;
62 int appAssemblyIndex = -1;
65 protected SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath)
66 : this (context, virtualPath, physicalPath, null)
69 internal SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath, TextReader reader)
72 cachedType = CachingCompiler.GetTypeFromCache (physicalPath);
73 if (cachedType != null)
74 return; // We don't need anything else.
76 // context is obsolete in 2.0+ - MSDN recommends passing null, so we need to
77 // take that into account
79 this.context = context;
81 this.context = HttpContext.Current;
83 this.vPath = virtualPath;
84 AddDependency (virtualPath);
86 // physicalPath is obsolete in 2.0+ - same note what for context applies here
87 if (physicalPath != null && physicalPath.Length > 0)
88 this.physPath = physicalPath;
90 HttpRequest req = this.context != null ? context.Request : null;
92 this.physPath = req.MapPath (virtualPath);
95 assemblies = new ArrayList ();
96 string location = Context.ApplicationInstance.AssemblyLocation;
97 if (location != typeof (TemplateParser).Assembly.Location)
98 appAssemblyIndex = assemblies.Add (location);
100 bool addAssembliesInBin = false;
101 foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
102 if (info.Assembly == "*")
103 addAssembliesInBin = true;
105 AddAssemblyByName (info.Assembly, null);
107 if (addAssembliesInBin)
108 AddAssembliesInBin ();
109 language = CompilationConfig.DefaultLanguage;
111 GetDirectivesAndContent ();
114 protected Type GetCompiledTypeFromCache ()
119 void GetDirectivesAndContent ()
122 bool directiveFound = false;
123 bool inDirective = false;
124 StringBuilder directive = null;
125 StringBuilder content = new StringBuilder ();
126 int idxStart, idxEnd, length;
130 sr = reader as StreamReader;
132 sr = new StreamReader (File.OpenRead (physPath), WebEncoding.FileEncoding);
135 while ((line = sr.ReadLine ()) != null && cachedType == null) {
136 length = line.Length;
138 content.Append ("\n");
142 idxStart = line.IndexOf ("<%");
144 idxEnd = line.IndexOf ("%>");
146 content.Append (line.Substring (0, idxStart));
148 if (directive == null)
149 directive = new StringBuilder ();
151 directive.Length = 0;
154 directiveFound = true;
156 directive.Append (line.Substring (idxStart, idxEnd - idxStart + 2));
157 if (idxEnd < length - 2)
158 content.Append (line.Substring (idxEnd + 2, length - idxEnd - 2));
161 directiveFound = false;
162 directive.Append (line.Substring (idxStart));
168 int idx = line.IndexOf ("%>");
170 directive.Append (line.Substring (0, idx + 2));
172 content.Append (line.Substring (idx + 2) + "\n");
174 directiveFound = true;
176 directive.Append (line);
181 if (directiveFound) {
182 ParseDirective (directive.ToString ());
183 directiveFound = false;
185 cachedType = CachingCompiler.GetTypeFromCache (physPath);
186 if (cachedType != null)
193 content.Append (line + "\n");
199 throw new ParseException (null, "No @" + DefaultDirectiveName +
202 if (cachedType == null)
203 this.program = content.ToString ();
206 void TagParsed (ILocation location, System.Web.Compilation.TagType tagtype, string tagid, TagAttributes attributes)
208 if (tagtype != System.Web.Compilation.TagType.Directive)
209 throw new ParseException (location, "Unexpected tag");
211 if (tagid == null || tagid.Length == 0 || String.Compare (tagid, DefaultDirectiveName, true, Helpers.InvariantCulture) == 0) {
212 AddDefaultDirective (location, attributes);
213 } else if (String.Compare (tagid, "Assembly", true, Helpers.InvariantCulture) == 0) {
214 AddAssemblyDirective (location, attributes);
216 throw new ParseException (location, "Unexpected directive: " + tagid);
220 void TextParsed (ILocation location, string text)
222 if (text.Trim () != "")
223 throw new ParseException (location, "Text not allowed here");
226 void ParseError (ILocation location, string message)
228 throw new ParseException (location, message);
231 static string GetAndRemove (Hashtable table, string key)
233 string o = table [key] as string;
238 void ParseDirective (string line)
242 using (StringReader input = new StringReader (line)) {
243 parser = new AspParser (physPath, input);
246 parser.Error += new ParseErrorHandler (ParseError);
247 parser.TagParsed += new TagParsedHandler (TagParsed);
248 parser.TextParsed += new TextParsedHandler (TextParsed);
253 internal virtual void AddDefaultDirective (ILocation location, TagAttributes attrs)
255 CompilationSection compConfig;
256 compConfig = CompilationConfig;
259 throw new ParseException (location, "duplicate " + DefaultDirectiveName + " directive");
262 Hashtable attributes = attrs.GetDictionary (null);
263 className = GetAndRemove (attributes, "class");
264 if (className == null)
265 throw new ParseException (null, "No Class attribute found.");
267 string d = GetAndRemove (attributes, "debug");
269 debug = (String.Compare (d, "true", true, Helpers.InvariantCulture) == 0);
270 if (debug == false && String.Compare (d, "false", true, Helpers.InvariantCulture) != 0)
271 throw new ParseException (null, "Invalid value for Debug attribute");
273 debug = compConfig.Debug;
275 language = GetAndRemove (attributes, "language");
276 if (language == null)
277 language = compConfig.DefaultLanguage;
279 GetAndRemove (attributes, "codebehind");
280 if (attributes.Count > 0)
281 throw new ParseException (location, "Unrecognized attribute in " +
282 DefaultDirectiveName + " directive");
285 internal virtual void AddAssemblyDirective (ILocation location, TagAttributes attrs)
287 Hashtable tbl = attrs.GetDictionary (null);
288 string name = GetAndRemove (tbl, "Name");
289 string src = GetAndRemove (tbl, "Src");
290 if (name == null && src == null)
291 throw new ParseException (location, "You gotta specify Src or Name");
293 if (name != null && src != null)
294 throw new ParseException (location, "Src and Name cannot be used together");
297 AddAssemblyByName (name, location);
299 GetAssemblyFromSource (src, location);
303 throw new ParseException (location, "Unrecognized attribute in Assembly directive");
306 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
308 if (assembly == null)
309 throw new ArgumentNullException ("assembly");
312 anames = new Hashtable ();
314 string name = assembly.GetName ().Name;
315 string loc = assembly.Location;
317 if (!assemblies.Contains (loc)) {
318 assemblies.Add (loc);
322 anames [loc] = assembly;
324 if (!assemblies.Contains (name)) {
325 assemblies.Add (name);
328 anames [name] = assembly;
332 internal virtual Assembly AddAssemblyByName (string name, ILocation location)
335 anames = new Hashtable ();
337 if (anames.Contains (name)) {
338 object o = anames [name];
345 Assembly assembly = LoadAssemblyFromBin (name);
346 if (assembly != null) {
347 AddAssembly (assembly, true);
353 assembly = Assembly.LoadWithPartialName (name);
354 } catch (Exception e) {
359 if (assembly == null)
360 throw new ParseException (location, String.Format ("Assembly '{0}' not found", name), ex);
362 AddAssembly (assembly, true);
366 void AddAssembliesInBin ()
369 foreach (string s in HttpApplication.BinDirectoryAssemblies) {
373 Assembly assembly = Assembly.LoadFrom (s);
374 AddAssembly (assembly, true);
375 } catch (FileLoadException e) {
378 } catch (BadImageFormatException e) {
381 } catch (Exception e) {
382 throw new Exception ("Error while loading " + s, e);
385 if (ex != null && HttpRuntime.IsDebuggingEnabled) {
386 Console.WriteLine ("**** DEBUG MODE *****");
387 Console.WriteLine ("Bad assembly found in bin/. Exception (ignored):");
388 Console.WriteLine (ex);
393 Assembly LoadAssemblyFromBin (string name)
395 Assembly assembly = null;
396 foreach (string dll in HttpApplication.BinDirectoryAssemblies) {
397 string fn = Path.GetFileName (dll);
398 fn = Path.ChangeExtension (fn, null);
402 assembly = Assembly.LoadFrom (dll);
409 Assembly GetAssemblyFromSource (string vpath, ILocation location)
411 vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
412 string realPath = context.Request.MapPath (vpath);
413 if (!File.Exists (realPath))
414 throw new ParseException (location, "File " + vpath + " not found");
416 AddDependency (vpath);
418 CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
419 if (result.NativeCompilerReturnValue != 0) {
420 using (StreamReader sr = new StreamReader (realPath)) {
421 throw new CompilationException (realPath, result.Errors, sr.ReadToEnd ());
425 AddAssembly (result.CompiledAssembly, true);
426 return result.CompiledAssembly;
429 internal Type GetTypeFromBin (string tname)
431 if (tname == null || tname.Length == 0)
432 throw new ArgumentNullException ("tname");
437 int comma = tname.IndexOf (',');
440 typeName = tname.Substring (0, comma).Trim ();
441 assemblyName = tname.Substring (comma + 1).Trim ();
448 Assembly assembly = null;
449 if (assemblyName != null) {
450 assembly = Assembly.Load (assemblyName);
451 if (assembly != null)
452 type = assembly.GetType (typeName, false);
457 IList toplevelAssemblies = BuildManager.TopLevelAssemblies;
458 if (toplevelAssemblies != null && toplevelAssemblies.Count > 0) {
459 foreach (Assembly asm in toplevelAssemblies) {
460 type = asm.GetType (typeName, false);
463 throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
469 foreach (string dll in HttpApplication.BinDirectoryAssemblies) {
471 assembly = Assembly.LoadFrom (dll);
472 } catch (FileLoadException) {
475 } catch (BadImageFormatException) {
480 type = assembly.GetType (typeName, false);
483 throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
491 throw new HttpException (String.Format ("Type {0} not found.", typeName));
496 internal virtual void AddDependency (string filename)
498 if (dependencies == null)
499 dependencies = new ArrayList ();
501 if (!dependencies.Contains (filename))
502 dependencies.Add (filename);
506 protected abstract string DefaultDirectiveName { get; }
508 internal HttpContext Context {
509 get { return context; }
512 internal string VirtualPath {
513 get { return vPath; }
516 internal string PhysicalPath {
517 get { return physPath; }
520 internal string ClassName {
521 get { return className; }
524 internal bool Debug {
525 get { return debug; }
528 internal string Language {
529 get { return language; }
532 internal string Program {
541 internal ArrayList Assemblies {
543 if (appAssemblyIndex != -1) {
544 object o = assemblies [appAssemblyIndex];
545 assemblies.RemoveAt (appAssemblyIndex);
547 appAssemblyIndex = -1;
554 internal ArrayList Dependencies {
555 get { return dependencies; }
558 internal string BaseDir {
561 baseDir = context.Request.MapPath (BaseVirtualDir);
567 internal virtual string BaseVirtualDir {
569 if (baseVDir == null)
570 baseVDir = UrlUtils.GetDirectory (context.Request.FilePath);
576 CompilationSection CompilationConfig {
578 string vp = VirtualPath;
579 if (String.IsNullOrEmpty (vp))
580 return WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
582 return WebConfigurationManager.GetSection ("system.web/compilation", vp) as CompilationSection;
586 internal TextReader Reader {
587 get { return reader; }
588 set { reader = value; }