* ObjectStateFormatter.cs: WriterContext.RegisterCache, refactored out parameter
[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                         StringBuilder content = new StringBuilder ();
123                         using (StreamReader reader = new StreamReader (File.OpenRead (physPath))) {
124                                 while ((line = reader.ReadLine ()) != null && cachedType == null) {
125                                         string trimmed = line.Trim ();
126                                         if (!directiveFound && trimmed == String.Empty)
127                                                 continue;
128                                         
129                                         if (trimmed.StartsWith ("<")) {
130                                                 ParseDirective (trimmed);
131                                                 directiveFound = true;
132                                                 if (gotDefault) {
133                                                         cachedType = CachingCompiler.GetTypeFromCache (physPath);
134                                                         if (cachedType != null)
135                                                                 break;
136                                                 }
137
138                                                 continue;
139                                         }
140
141                                         content.Append (line + "\n");
142                                         content.Append (reader.ReadToEnd ());
143                                 }
144                         }
145
146                         if (!gotDefault)
147                                 throw new ParseException (null, "No @" + DefaultDirectiveName +
148                                                         " directive found");
149
150                         if (cachedType == null)
151                                 this.program = content.ToString ();
152                 }
153
154                 void TagParsed (ILocation location, System.Web.Compilation.TagType tagtype, string tagid, TagAttributes attributes)
155                 {
156                         if (tagtype != System.Web.Compilation.TagType.Directive)
157                                 throw new ParseException (location, "Unexpected tag");
158
159                         if (String.Compare (tagid, DefaultDirectiveName, true) == 0) {
160                                 AddDefaultDirective (location, attributes);
161                         } else if (String.Compare (tagid, "Assembly", true) == 0) {
162                                 AddAssemblyDirective (location, attributes);
163                         } else {
164                                 throw new ParseException (location, "Unexpected directive: " + tagid);
165                         }
166                 }
167
168                 void TextParsed (ILocation location, string text)
169                 {
170                         if (text.Trim () != "")
171                                 throw new ParseException (location, "Text not allowed here");
172                 }
173
174                 void ParseError (ILocation location, string message)
175                 {
176                         throw new ParseException (location, message);
177                 }
178
179                 static string GetAndRemove (Hashtable table, string key)
180                 {
181                         string o = table [key] as string;
182                         table.Remove (key);
183                         return o;
184                 }
185                 
186                 void ParseDirective (string line)
187                 {
188                         AspParser parser = new AspParser (physPath, new StringReader (line));
189                         parser.Error += new ParseErrorHandler (ParseError);
190                         parser.TagParsed += new TagParsedHandler (TagParsed);
191                         parser.TextParsed += new TextParsedHandler (TextParsed);
192
193                         parser.Parse ();
194                 }
195
196                 internal virtual void AddDefaultDirective (ILocation location, TagAttributes attrs)
197                 {
198                         if (gotDefault)
199                                 throw new ParseException (location, "duplicate " + DefaultDirectiveName + " directive");
200
201                         gotDefault = true;
202                         Hashtable attributes = attrs.GetDictionary (null);
203                         className = GetAndRemove (attributes, "class");
204                         if (className == null)
205                                 throw new ParseException (null, "No Class attribute found.");
206                         
207                         string d = GetAndRemove (attributes, "debug");
208                         if (d != null) {
209                                 debug = (String.Compare (d, "true", true) == 0);
210                                 if (debug == false && String.Compare (d, "false", true) != 0)
211                                         throw new ParseException (null, "Invalid value for Debug attribute");
212                         }
213
214                         language = GetAndRemove (attributes, "language");
215                         if (language == null)
216                                 language = CompilationConfig.DefaultLanguage;
217
218                         GetAndRemove (attributes, "codebehind");
219                         if (attributes.Count > 0)
220                                 throw new ParseException (location, "Unrecognized attribute in " +
221                                                           DefaultDirectiveName + " directive");
222                 }
223
224                 internal virtual void AddAssemblyDirective (ILocation location, TagAttributes attrs)
225                 {
226                         Hashtable tbl = attrs.GetDictionary (null);
227                         string name = GetAndRemove (tbl, "Name");
228                         string src = GetAndRemove (tbl, "Src");
229                         if (name == null && src == null)
230                                 throw new ParseException (location, "You gotta specify Src or Name");
231
232                         if (name != null && src != null)
233                                 throw new ParseException (location, "Src and Name cannot be used together");
234
235                         if (name != null) {
236                                 AddAssemblyByName (name, location);
237                         } else {
238                                 GetAssemblyFromSource (src, location);
239                         }
240
241                         if (tbl.Count > 0)
242                                 throw new ParseException (location, "Unrecognized attribute in Assembly directive");
243                 }
244
245                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
246                 {
247                         if (anames == null)
248                                 anames = new Hashtable ();
249
250                         string name = assembly.GetName ().Name;
251                         string loc = assembly.Location;
252                         if (fullPath) {
253                                 if (!assemblies.Contains (loc)) {
254                                         assemblies.Add (loc);
255                                 }
256
257                                 anames [name] = loc;
258                                 anames [loc] = assembly;
259                         } else {
260                                 if (!assemblies.Contains (name)) {
261                                         assemblies.Add (name);
262                                 }
263
264                                 anames [name] = assembly;
265                         }
266                 }
267
268                 internal virtual Assembly AddAssemblyByName (string name, ILocation location)
269                 {
270                         if (anames == null)
271                                 anames = new Hashtable ();
272
273                         if (anames.Contains (name)) {
274                                 object o = anames [name];
275                                 if (o is string)
276                                         o = anames [o];
277
278                                 return (Assembly) o;
279                         }
280
281                         Assembly assembly = LoadAssemblyFromBin (name);
282                         if (assembly != null) {
283                                 AddAssembly (assembly, true);
284                                 return assembly;
285                         }
286
287                         try {
288                                 assembly = Assembly.LoadWithPartialName (name);
289                         } catch (Exception e) {
290                                 throw new ParseException (location, "Assembly " + name + " not found", e);
291                         }
292
293                         AddAssembly (assembly, true);
294                         return assembly;
295                 }
296
297                 void AddAssembliesInBin ()
298                 {
299                         if (!Directory.Exists (PrivateBinPath))
300                                 return;
301
302                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
303                         foreach (string dll in binDlls) {
304                                 try {
305                                         Assembly assembly = Assembly.LoadFrom (dll);
306                                         AddAssembly (assembly, true);
307                                 } catch (Exception e) {
308                                         throw new Exception ("Error while loading " + dll, e);
309                                 }
310                         }
311                 }
312
313                 Assembly LoadAssemblyFromBin (string name)
314                 {
315                         Assembly assembly;
316                         if (!Directory.Exists (PrivateBinPath))
317                                 return null;
318
319                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
320                         foreach (string dll in binDlls) {
321                                 string fn = Path.GetFileName (dll);
322                                 fn = Path.ChangeExtension (fn, null);
323                                 if (fn != name)
324                                         continue;
325
326                                 assembly = Assembly.LoadFrom (dll);
327                                 return assembly;
328                         }
329
330                         return null;
331                 }
332
333                 Assembly GetAssemblyFromSource (string vpath, ILocation location)
334                 {
335                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
336                         string realPath = context.Request.MapPath (vpath);
337                         if (!File.Exists (realPath))
338                                 throw new ParseException (location, "File " + vpath + " not found");
339
340                         AddDependency (realPath);
341
342                         CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
343                         if (result.NativeCompilerReturnValue != 0) {
344                                 StreamReader reader = new StreamReader (realPath);
345                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
346                         }
347
348                         AddAssembly (result.CompiledAssembly, true);
349                         return result.CompiledAssembly;
350                 }
351                 
352                 internal Type GetTypeFromBin (string typeName)
353                 {
354                         Type result = null;
355 #if NET_2_0
356                         IList toplevelAssemblies = BuildManager.TopLevelAssemblies;
357                         if (toplevelAssemblies != null && toplevelAssemblies.Count > 0) {
358                                 foreach (Assembly asm in toplevelAssemblies) {
359                                         Type type = asm.GetType (typeName, false);
360                                         if (type != null) {
361                                                 if (result != null)
362                                                         throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
363                                         }
364                                         result = type;
365                                 }
366                         }
367 #endif
368                         if (!Directory.Exists (PrivateBinPath))
369                                 if (result == null)
370                                         throw new HttpException (String.Format ("Type {0} not found.", typeName));
371                                 else
372                                         return result;
373                         
374                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
375                         foreach (string dll in binDlls) {
376                                 Assembly assembly = Assembly.LoadFrom (dll);
377                                 Type type = assembly.GetType (typeName, false);
378                                 if (type != null) {
379                                         if (result != null) 
380                                                 throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
381
382                                         result = type;
383                                 } 
384                         }
385
386                         if (result == null)
387                                 throw new HttpException (String.Format ("Type {0} not found.", typeName));
388
389                         return result;
390                 }
391                 
392                 internal virtual void AddDependency (string filename)
393                 {
394                         if (dependencies == null)
395                                 dependencies = new ArrayList ();
396
397                         if (!dependencies.Contains (filename))
398                                 dependencies.Add (filename);
399                 }
400                 
401                 // Properties
402                 protected abstract string DefaultDirectiveName { get; }
403
404                 internal HttpContext Context {
405                         get { return context; }
406                 }
407
408                 internal string VirtualPath {
409                         get { return vPath; }
410                 }
411
412                 internal string PhysicalPath {
413                         get { return physPath; }
414                 }
415
416                 internal string ClassName {
417                         get { return className; }
418                 }
419
420                 internal bool Debug {
421                         get { return debug; }
422                 }
423
424                 internal string Language {
425                         get { return language; }
426                 }
427
428                 internal string Program {
429                         get { return program; }
430                 }
431
432                 internal ArrayList Assemblies {
433                         get {
434                                 if (appAssemblyIndex != -1) {
435                                         object o = assemblies [appAssemblyIndex];
436                                         assemblies.RemoveAt (appAssemblyIndex);
437                                         assemblies.Add (o);
438                                         appAssemblyIndex = -1;
439                                 }
440
441                                 return assemblies;
442                         }
443                 }
444
445                 internal ArrayList Dependencies {
446                         get { return dependencies; }
447                 }
448
449                 internal string PrivateBinPath {
450                         get {
451                                 if (privateBinPath != null)
452                                         return privateBinPath;
453
454                                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
455                                 privateBinPath = setup.PrivateBinPath;
456                                         
457                                 if (!Path.IsPathRooted (privateBinPath)) {
458                                         string appbase = setup.ApplicationBase;
459                                         if (appbase.StartsWith ("file://")) {
460                                                 appbase = appbase.Substring (7);
461                                                 if (Path.DirectorySeparatorChar != '/')
462                                                         appbase = appbase.Replace ('/', Path.DirectorySeparatorChar);
463                                         }
464                                         privateBinPath = Path.Combine (appbase, privateBinPath);
465                                 }
466
467                                 return privateBinPath;
468                         }
469                 }
470
471                 internal string BaseDir {
472                         get {
473                                 if (baseDir == null)
474                                         baseDir = context.Request.MapPath (BaseVirtualDir);
475
476                                 return baseDir;
477                         }
478                 }
479
480                 internal virtual string BaseVirtualDir {
481                         get {
482                                 if (baseVDir == null)
483                                         baseVDir = UrlUtils.GetDirectory (context.Request.FilePath);
484
485                                 return baseVDir;
486                         }
487                 }
488
489 #if NET_2_0
490                 CompilationSection CompilationConfig {
491                         get {
492                                 return (CompilationSection)WebConfigurationManager.GetSection ("system.web/compilation");
493                         }
494                 }
495
496                 internal TextReader Reader {
497                         get { return reader; }
498                         set { reader = value; }
499                 }
500 #else
501                 internal CompilationConfiguration CompilationConfig {
502                         get {
503                                 if (compilationConfig == null)
504                                         compilationConfig = CompilationConfiguration.GetInstance (context);
505
506                                 return compilationConfig;
507                         }
508                 }
509 #endif
510         }
511 }
512