2007-12-13 Igor Zelmanovich <igorz@mainsoft.com>
[mono.git] / mcs / class / System.Web / System.Web.UI / SimpleWebHandlerParser.cs
1 //
2 // System.Web.UI.SimpleWebHandlerParser
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 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.
28 //
29
30 using System.CodeDom.Compiler;
31 using System.Collections;
32 using System.IO;
33 using System.Reflection;
34 using System.Security.Permissions;
35 using System.Text;
36 using System.Web.Compilation;
37 using System.Web.Configuration;
38 using System.Web.Util;
39
40 namespace System.Web.UI
41 {
42         // CAS
43         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
44         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45         public abstract class SimpleWebHandlerParser
46         {
47                 HttpContext context;
48                 string vPath;
49                 string physPath;
50                 string className;
51                 bool debug;
52                 string language;
53                 string program;
54                 bool gotDefault;
55                 ArrayList assemblies;
56                 ArrayList dependencies;
57                 Hashtable anames;
58                 string privateBinPath;
59                 string baseDir;
60                 string baseVDir;
61 #if !NET_2_0
62                 CompilationConfiguration compilationConfig;
63 #else
64                 TextReader reader;
65 #endif
66                 int appAssemblyIndex = -1;
67                 Type cachedType;
68
69                 protected SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath)
70                 {
71                         cachedType = CachingCompiler.GetTypeFromCache (physicalPath);
72                         if (cachedType != null)
73                                 return; // We don't need anything else.
74
75                         this.context = context;
76                         this.vPath = virtualPath;
77                         this.physPath = physicalPath;
78                         AddDependency (physicalPath);
79
80                         assemblies = new ArrayList ();
81                         string location = Context.ApplicationInstance.AssemblyLocation;
82                         if (location != typeof (TemplateParser).Assembly.Location)
83                                 appAssemblyIndex = assemblies.Add (location);
84
85 #if NET_2_0
86                         bool addAssembliesInBin = false;
87                         foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
88                                 if (info.Assembly == "*")
89                                         addAssembliesInBin = true;
90                                 else
91                                         AddAssemblyByName (info.Assembly, null);
92                         }
93                         if (addAssembliesInBin)
94                                 AddAssembliesInBin ();
95 #else
96                         assemblies.AddRange (CompilationConfig.Assemblies);
97                         if (CompilationConfig.AssembliesInBin)
98                                 AddAssembliesInBin ();
99 #endif
100
101                         language = CompilationConfig.DefaultLanguage;
102
103                         GetDirectivesAndContent ();
104                 }
105 #if NET_2_0
106                 internal SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath, TextReader reader)
107                         : this (context, virtualPath, physicalPath)
108                 {
109                         this.reader = reader;
110                 }
111 #endif
112
113                 protected Type GetCompiledTypeFromCache ()
114                 {
115                         return cachedType;
116                 }
117
118                 void GetDirectivesAndContent ()
119                 {
120                         string line;
121                         bool directiveFound = false;
122                         bool inDirective = false;
123                         StringBuilder directive = null;
124                         StringBuilder content = new StringBuilder ();
125                         int idxStart, idxEnd, length;                   
126
127                         using (StreamReader reader = new StreamReader (File.OpenRead (physPath))) {
128                                 while ((line = reader.ReadLine ()) != null && cachedType == null) {
129                                         length = line.Length;
130                                         if (length == 0) {
131                                                 content.Append ("\n");
132                                                 continue;
133                                         }
134                                         
135                                         idxStart = line.IndexOf ("<%");
136                                         if (idxStart > -1) {
137                                                 idxEnd = line.IndexOf ("%>");                                           
138                                                 if (idxStart > 0)
139                                                         content.Append (line.Substring (0, idxStart));
140
141                                                 if (directive == null)
142                                                         directive = new StringBuilder ();
143                                                 else
144                                                         directive.Length = 0;
145                                                 
146                                                 if (idxEnd > -1) {
147                                                         directiveFound = true;
148                                                         inDirective = false;
149                                                         directive.Append (line.Substring (idxStart, idxEnd - idxStart + 2));
150                                                         if (idxEnd < length - 2)
151                                                                 content.Append (line.Substring (idxEnd + 2, length - idxEnd - 2));
152                                                 } else {
153                                                         inDirective = true;
154                                                         directiveFound = false;
155                                                         directive.Append (line.Substring (idxStart));
156                                                         continue;
157                                                 }
158                                         }
159
160                                         if (inDirective) {
161                                                 int idx = line.IndexOf ("%>");
162                                                 if (idx > -1) {
163                                                         directive.Append (line.Substring (0, idx + 2));
164                                                         if (idx < length)
165                                                                 content.Append (line.Substring (idx + 2) + "\n");
166                                                         inDirective = false;
167                                                         directiveFound = true;
168                                                 } else {
169                                                         directive.Append (line);
170                                                         continue;
171                                                 }
172                                         }
173                                         
174                                         if (directiveFound) {
175                                                 ParseDirective (directive.ToString ());
176                                                 directiveFound = false;
177                                                 if (gotDefault) {
178                                                         cachedType = CachingCompiler.GetTypeFromCache (physPath);
179                                                         if (cachedType != null)
180                                                                 break;
181                                                 }
182
183                                                 continue;
184                                         }
185
186                                         content.Append (line + "\n");
187                                 }
188                                 directive = null;
189                         }
190
191                         if (!gotDefault)
192                                 throw new ParseException (null, "No @" + DefaultDirectiveName +
193                                                         " directive found");
194
195                         if (cachedType == null)
196                                 this.program = content.ToString ();
197                 }
198
199                 void TagParsed (ILocation location, System.Web.Compilation.TagType tagtype, string tagid, TagAttributes attributes)
200                 {
201                         if (tagtype != System.Web.Compilation.TagType.Directive)
202                                 throw new ParseException (location, "Unexpected tag");
203
204                         if (String.Compare (tagid, DefaultDirectiveName, true) == 0) {
205                                 AddDefaultDirective (location, attributes);
206                         } else if (String.Compare (tagid, "Assembly", true) == 0) {
207                                 AddAssemblyDirective (location, attributes);
208                         } else {
209                                 throw new ParseException (location, "Unexpected directive: " + tagid);
210                         }
211                 }
212
213                 void TextParsed (ILocation location, string text)
214                 {
215                         if (text.Trim () != "")
216                                 throw new ParseException (location, "Text not allowed here");
217                 }
218
219                 void ParseError (ILocation location, string message)
220                 {
221                         throw new ParseException (location, message);
222                 }
223
224                 static string GetAndRemove (Hashtable table, string key)
225                 {
226                         string o = table [key] as string;
227                         table.Remove (key);
228                         return o;
229                 }
230                 
231                 void ParseDirective (string line)
232                 {
233                         AspParser parser = new AspParser (physPath, new StringReader (line));
234                         parser.Error += new ParseErrorHandler (ParseError);
235                         parser.TagParsed += new TagParsedHandler (TagParsed);
236                         parser.TextParsed += new TextParsedHandler (TextParsed);
237
238                         parser.Parse ();
239                 }
240
241                 internal virtual void AddDefaultDirective (ILocation location, TagAttributes attrs)
242                 {
243                         if (gotDefault)
244                                 throw new ParseException (location, "duplicate " + DefaultDirectiveName + " directive");
245
246                         gotDefault = true;
247                         Hashtable attributes = attrs.GetDictionary (null);
248                         className = GetAndRemove (attributes, "class");
249                         if (className == null)
250                                 throw new ParseException (null, "No Class attribute found.");
251                         
252                         string d = GetAndRemove (attributes, "debug");
253                         if (d != null) {
254                                 debug = (String.Compare (d, "true", true) == 0);
255                                 if (debug == false && String.Compare (d, "false", true) != 0)
256                                         throw new ParseException (null, "Invalid value for Debug attribute");
257                         }
258
259                         language = GetAndRemove (attributes, "language");
260                         if (language == null)
261                                 language = CompilationConfig.DefaultLanguage;
262
263                         GetAndRemove (attributes, "codebehind");
264                         if (attributes.Count > 0)
265                                 throw new ParseException (location, "Unrecognized attribute in " +
266                                                           DefaultDirectiveName + " directive");
267                 }
268
269                 internal virtual void AddAssemblyDirective (ILocation location, TagAttributes attrs)
270                 {
271                         Hashtable tbl = attrs.GetDictionary (null);
272                         string name = GetAndRemove (tbl, "Name");
273                         string src = GetAndRemove (tbl, "Src");
274                         if (name == null && src == null)
275                                 throw new ParseException (location, "You gotta specify Src or Name");
276
277                         if (name != null && src != null)
278                                 throw new ParseException (location, "Src and Name cannot be used together");
279
280                         if (name != null) {
281                                 AddAssemblyByName (name, location);
282                         } else {
283                                 GetAssemblyFromSource (src, location);
284                         }
285
286                         if (tbl.Count > 0)
287                                 throw new ParseException (location, "Unrecognized attribute in Assembly directive");
288                 }
289
290                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
291                 {
292                         if (assembly == null)
293                                 throw new ArgumentNullException ("assembly");
294                         
295                         if (anames == null)
296                                 anames = new Hashtable ();
297
298                         string name = assembly.GetName ().Name;
299                         string loc = assembly.Location;
300                         if (fullPath) {
301                                 if (!assemblies.Contains (loc)) {
302                                         assemblies.Add (loc);
303                                 }
304
305                                 anames [name] = loc;
306                                 anames [loc] = assembly;
307                         } else {
308                                 if (!assemblies.Contains (name)) {
309                                         assemblies.Add (name);
310                                 }
311
312                                 anames [name] = assembly;
313                         }
314                 }
315
316                 internal virtual Assembly AddAssemblyByName (string name, ILocation location)
317                 {
318                         if (anames == null)
319                                 anames = new Hashtable ();
320
321                         if (anames.Contains (name)) {
322                                 object o = anames [name];
323                                 if (o is string)
324                                         o = anames [o];
325
326                                 return (Assembly) o;
327                         }
328
329                         Assembly assembly = LoadAssemblyFromBin (name);
330                         if (assembly != null) {
331                                 AddAssembly (assembly, true);
332                                 return assembly;
333                         }
334
335                         Exception ex = null;
336                         try {
337                                 assembly = Assembly.LoadWithPartialName (name);
338                         } catch (Exception e) {
339                                 ex = null;
340                                 assembly = null;
341                         }
342
343                         if (assembly == null)
344                                 throw new ParseException (location, String.Format ("Assembly '{0}' not found", name), ex);
345                         
346                         AddAssembly (assembly, true);
347                         return assembly;
348                 }
349
350                 void AddAssembliesInBin ()
351                 {
352                         foreach (string s in HttpApplication.BinDirectoryAssemblies) {
353                                 try {
354                                         Assembly assembly = Assembly.LoadFrom (s);
355                                         AddAssembly (assembly, true);
356                                 } catch (Exception e) {
357                                         throw new Exception ("Error while loading " + s, e);
358                                 }
359                         }
360                 }
361
362                 Assembly LoadAssemblyFromBin (string name)
363                 {
364                         Assembly assembly = null;
365                         foreach (string dll in HttpApplication.BinDirectoryAssemblies) {
366                                 string fn = Path.GetFileName (dll);
367                                 fn = Path.ChangeExtension (fn, null);
368                                 if (fn != name)
369                                         continue;
370
371                                 assembly = Assembly.LoadFrom (dll);
372                                 return assembly;
373                         }
374                         
375                         return null;
376                 }
377
378                 Assembly GetAssemblyFromSource (string vpath, ILocation location)
379                 {
380                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
381                         string realPath = context.Request.MapPath (vpath);
382                         if (!File.Exists (realPath))
383                                 throw new ParseException (location, "File " + vpath + " not found");
384
385                         AddDependency (realPath);
386
387                         CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
388                         if (result.NativeCompilerReturnValue != 0) {
389                                 StreamReader reader = new StreamReader (realPath);
390                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
391                         }
392
393                         AddAssembly (result.CompiledAssembly, true);
394                         return result.CompiledAssembly;
395                 }
396                 
397                 internal Type GetTypeFromBin (string tname)
398                 {
399                         if (tname == null || tname.Length == 0)
400                                 throw new ArgumentNullException ("tname");
401                         
402                         Type result = null;
403                         string typeName;
404                         string assemblyName;
405                         int comma = tname.IndexOf (',');
406                         
407                         if (comma != -1) {
408                                 typeName = tname.Substring (0, comma).Trim ();
409                                 assemblyName = tname.Substring (comma + 1).Trim ();
410                         } else {
411                                 typeName = tname;
412                                 assemblyName = null;
413                         }
414
415                         Type type = null;
416                         Assembly assembly = null;
417                         if (assemblyName != null) {
418                                 assembly = Assembly.Load (assemblyName);
419                                 if (assembly != null)
420                                         type = assembly.GetType (typeName, false);
421                                 if (type != null)
422                                         return type;
423                         }
424                         
425 #if NET_2_0
426                         IList toplevelAssemblies = BuildManager.TopLevelAssemblies;
427                         if (toplevelAssemblies != null && toplevelAssemblies.Count > 0) {
428                                 foreach (Assembly asm in toplevelAssemblies) {
429                                         type = asm.GetType (typeName, false);
430                                         if (type != null) {
431                                                 if (result != null)
432                                                         throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
433                                                 result = type;
434                                         }
435                                 }
436                         }
437 #endif
438
439                         foreach (string dll in HttpApplication.BinDirectoryAssemblies) {
440                                 assembly = Assembly.LoadFrom (dll);
441                                 type = assembly.GetType (typeName, false);
442                                 if (type != null) {
443                                         if (result != null) 
444                                                 throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
445                                                 
446                                         result = type;
447                                 }
448                         }
449
450                         
451                         if (result == null)
452                                 throw new HttpException (String.Format ("Type {0} not found.", typeName));
453
454                         return result;
455                 }
456                 
457                 internal virtual void AddDependency (string filename)
458                 {
459                         if (dependencies == null)
460                                 dependencies = new ArrayList ();
461
462                         if (!dependencies.Contains (filename))
463                                 dependencies.Add (filename);
464                 }
465                 
466                 // Properties
467                 protected abstract string DefaultDirectiveName { get; }
468
469                 internal HttpContext Context {
470                         get { return context; }
471                 }
472
473                 internal string VirtualPath {
474                         get { return vPath; }
475                 }
476
477                 internal string PhysicalPath {
478                         get { return physPath; }
479                 }
480
481                 internal string ClassName {
482                         get { return className; }
483                 }
484
485                 internal bool Debug {
486                         get { return debug; }
487                 }
488
489                 internal string Language {
490                         get { return language; }
491                 }
492
493                 internal string Program {
494                         get { return program; }
495                 }
496
497                 internal ArrayList Assemblies {
498                         get {
499                                 if (appAssemblyIndex != -1) {
500                                         object o = assemblies [appAssemblyIndex];
501                                         assemblies.RemoveAt (appAssemblyIndex);
502                                         assemblies.Add (o);
503                                         appAssemblyIndex = -1;
504                                 }
505
506                                 return assemblies;
507                         }
508                 }
509
510                 internal ArrayList Dependencies {
511                         get { return dependencies; }
512                 }
513
514                 internal string BaseDir {
515                         get {
516                                 if (baseDir == null)
517                                         baseDir = context.Request.MapPath (BaseVirtualDir);
518
519                                 return baseDir;
520                         }
521                 }
522
523                 internal virtual string BaseVirtualDir {
524                         get {
525                                 if (baseVDir == null)
526                                         baseVDir = UrlUtils.GetDirectory (context.Request.FilePath);
527
528                                 return baseVDir;
529                         }
530                 }
531
532 #if NET_2_0
533                 CompilationSection CompilationConfig {
534                         get {
535                                 return (CompilationSection)WebConfigurationManager.GetSection ("system.web/compilation");
536                         }
537                 }
538
539                 internal TextReader Reader {
540                         get { return reader; }
541                         set { reader = value; }
542                 }
543 #else
544                 internal CompilationConfiguration CompilationConfig {
545                         get {
546                                 if (compilationConfig == null)
547                                         compilationConfig = CompilationConfiguration.GetInstance (context);
548
549                                 return compilationConfig;
550                         }
551                 }
552 #endif
553         }
554 }
555