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