Improve error handling
[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 //
9
10 using System;
11 using System.CodeDom.Compiler;
12 using System.Collections;
13 using System.IO;
14 using System.Reflection;
15 using System.Text;
16 using System.Web;
17 using System.Web.Compilation;
18 using System.Web.Configuration;
19 using System.Web.Util;
20
21 namespace System.Web.UI
22 {
23         public abstract class SimpleWebHandlerParser
24         {
25                 HttpContext context;
26                 string vPath;
27                 string physPath;
28                 string className;
29                 string codeBehind;
30                 bool debug;
31                 string language;
32                 string program;
33                 bool gotDefault;
34                 ArrayList assemblies;
35                 ArrayList dependencies;
36                 Hashtable anames;
37                 string privateBinPath;
38                 string baseDir;
39                 string baseVDir;
40                 CompilationConfiguration compilationConfig;
41
42                 protected SimpleWebHandlerParser (HttpContext context, string virtualPath, string physicalPath)
43                 {
44                         this.context = context;
45                         this.vPath = virtualPath;
46                         this.physPath = physicalPath;
47                         AddDependency (physicalPath);
48
49                         assemblies = new ArrayList ();
50                         string location = Context.ApplicationInstance.AssemblyLocation;
51                         if (location != typeof (TemplateParser).Assembly.Location)
52                                 assemblies.Add (location);
53
54                         assemblies.AddRange (CompilationConfig.Assemblies);
55                         if (CompilationConfig.AssembliesInBin)
56                                 AddAssembliesInBin ();
57
58                         language = CompilationConfig.DefaultLanguage;
59
60                         GetDirectivesAndContent ();
61                 }
62
63                 void GetDirectivesAndContent ()
64                 {
65                         StreamReader reader = new StreamReader (File.OpenRead (physPath));
66                         string line;
67                         bool directiveFound = false;
68                         StringBuilder content = new StringBuilder ();
69
70                         while ((line = reader.ReadLine ()) != null) {
71                                 string trimmed = line.Trim ();
72                                 if (!directiveFound && trimmed == String.Empty)
73                                         continue;
74                                 
75                                 if (trimmed.StartsWith ("<")) {
76                                         ParseDirective (trimmed);
77                                         directiveFound = true;
78                                         continue;
79                                 }
80
81                                 content.Append (line + "\n");
82                                 content.Append (reader.ReadToEnd ());
83                         }
84
85                         if (!gotDefault)
86                                 throw new ParseException (null, "No @WebService directive found");
87                                 
88                         this.program = content.ToString ();
89                         reader.Close ();
90                 }
91
92                 void TagParsed (ILocation location, System.Web.Compilation.TagType tagtype, string tagid, TagAttributes attributes)
93                 {
94                         if (tagtype != System.Web.Compilation.TagType.Directive)
95                                 throw new ParseException (location, "Unexpected tag");
96
97                         if (String.Compare (tagid, DefaultDirectiveName, true) == 0) {
98                                 AddDefaultDirective (location, attributes);
99                         } else if (String.Compare (tagid, "Assembly", true) == 0) {
100                                 AddAssemblyDirective (location, attributes);
101                         } else {
102                                 throw new ParseException (location, "Unexpected directive: " + tagid);
103                         }
104                 }
105
106                 void TextParsed (ILocation location, string text)
107                 {
108                         if (text.Trim () != "")
109                                 throw new ParseException (location, "Text not allowed here");
110                 }
111
112                 void ParseError (ILocation location, string message)
113                 {
114                         throw new ParseException (location, message);
115                 }
116
117                 static string GetAndRemove (Hashtable table, string key)
118                 {
119                         string o = table [key] as string;
120                         table.Remove (key);
121                         return o;
122                 }
123                 
124                 void ParseDirective (string line)
125                 {
126                         AspParser parser = new AspParser (physPath, new StringReader (line));
127                         parser.Error += new ParseErrorHandler (ParseError);
128                         parser.TagParsed += new TagParsedHandler (TagParsed);
129                         parser.TextParsed += new TextParsedHandler (TextParsed);
130
131                         parser.Parse ();
132                 }
133
134                 internal virtual void AddDefaultDirective (ILocation location, TagAttributes attrs)
135                 {
136                         if (gotDefault)
137                                 throw new ParseException (location, "duplicate " + DefaultDirectiveName + " directive");
138
139                         gotDefault = true;
140                         Hashtable attributes = attrs.GetDictionary (null);
141                         className = GetAndRemove (attributes, "class");
142                         if (className == null)
143                                 throw new ParseException (null, "No Class attribute found.");
144                         
145                         string d = GetAndRemove (attributes, "debug");
146                         if (d != null) {
147                                 debug = (String.Compare (d, "true", true) == 0);
148                                 if (debug == false && String.Compare (d, "false", true) != 0)
149                                         throw new ParseException (null, "Invalid value for Debug attribute");
150                         }
151
152                         language = GetAndRemove (attributes, "language");
153                         if (language == null)
154                                 language = CompilationConfig.DefaultLanguage;
155
156                         codeBehind = GetAndRemove (attributes, "codebehind");
157                         if (attributes.Count > 0)
158                                 throw new ParseException (location, "Unrecognized attribute in " +
159                                                           DefaultDirectiveName + " directive");
160                 }
161
162                 internal virtual void AddAssemblyDirective (ILocation location, TagAttributes attrs)
163                 {
164                         Hashtable tbl = attrs.GetDictionary (null);
165                         string name = GetAndRemove (tbl, "Name");
166                         string src = GetAndRemove (tbl, "Src");
167                         if (name == null && src == null)
168                                 throw new ParseException (location, "You gotta specify Src or Name");
169
170                         if (name != null && src != null)
171                                 throw new ParseException (location, "Src and Name cannot be used together");
172
173                         if (name != null) {
174                                 AddAssemblyByName (name, location);
175                         } else {
176                                 GetAssemblyFromSource (src, location);
177                         }
178
179                         if (tbl.Count > 0)
180                                 throw new ParseException (location, "Unrecognized attribute in Assembly directive");
181                 }
182
183                 internal virtual void AddAssembly (Assembly assembly, bool fullPath)
184                 {
185                         if (anames == null)
186                                 anames = new Hashtable ();
187
188                         string name = assembly.GetName ().Name;
189                         string loc = assembly.Location;
190                         if (fullPath) {
191                                 if (!assemblies.Contains (loc)) {
192                                         assemblies.Add (loc);
193                                 }
194
195                                 anames [name] = loc;
196                                 anames [loc] = assembly;
197                         } else {
198                                 if (!assemblies.Contains (name)) {
199                                         assemblies.Add (name);
200                                 }
201
202                                 anames [name] = assembly;
203                         }
204                 }
205
206                 internal virtual Assembly AddAssemblyByName (string name, ILocation location)
207                 {
208                         if (anames == null)
209                                 anames = new Hashtable ();
210
211                         if (anames.Contains (name)) {
212                                 object o = anames [name];
213                                 if (o is string)
214                                         o = anames [o];
215
216                                 return (Assembly) o;
217                         }
218
219                         bool fullpath = true;
220                         Assembly assembly = LoadAssemblyFromBin (name);
221                         if (assembly != null) {
222                                 AddAssembly (assembly, fullpath);
223                                 return assembly;
224                         }
225
226                         try {
227                                 assembly = Assembly.Load (name);
228                                 string loc = assembly.Location;
229                                 fullpath = (Path.GetDirectoryName (loc) == PrivateBinPath);
230                         } catch (Exception e) {
231                                 throw new ParseException (location, "Assembly " + name + " not found", e);
232                         }
233
234                         AddAssembly (assembly, fullpath);
235                         return assembly;
236                 }
237
238                 void AddAssembliesInBin ()
239                 {
240                         if (!Directory.Exists (PrivateBinPath))
241                                 return;
242
243                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
244                         foreach (string dll in binDlls) {
245                                 try {
246                                         Assembly assembly = Assembly.LoadFrom (dll);
247                                         AddAssembly (assembly, true);
248                                 } catch (Exception e) {
249                                         throw new Exception ("Error while loading " + dll, e);
250                                 }
251                         }
252                 }
253
254                 Assembly LoadAssemblyFromBin (string name)
255                 {
256                         Assembly assembly;
257                         if (!Directory.Exists (PrivateBinPath))
258                                 return null;
259
260                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
261                         foreach (string dll in binDlls) {
262                                 string fn = Path.GetFileName (dll);
263                                 fn = Path.ChangeExtension (fn, null);
264                                 if (fn != name)
265                                         continue;
266
267                                 assembly = Assembly.LoadFrom (dll);
268                                 return assembly;
269                         }
270
271                         return null;
272                 }
273
274                 Assembly GetAssemblyFromSource (string vpath, ILocation location)
275                 {
276                         vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
277                         string realPath = context.Request.MapPath (vpath);
278                         if (!File.Exists (realPath))
279                                 throw new ParseException (location, "File " + vpath + " not found");
280
281                         AddDependency (realPath);
282
283                         CompilerResults result = CachingCompiler.Compile (realPath, realPath, assemblies);
284                         if (result.NativeCompilerReturnValue != 0) {
285                                 StreamReader reader = new StreamReader (realPath);
286                                 throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
287                         }
288
289                         AddAssembly (result.CompiledAssembly, true);
290                         return result.CompiledAssembly;
291                 }
292                 
293                 internal Type GetTypeFromBin (string typeName)
294                 {
295                         if (!Directory.Exists (PrivateBinPath))
296                                 throw new HttpException (String.Format ("Type {0} not found.", typeName));
297
298                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
299                         Type result = null;
300                         foreach (string dll in binDlls) {
301                                 Assembly assembly = Assembly.LoadFrom (dll);
302                                 Type type = assembly.GetType (typeName, false);
303                                 if (type != null) {
304                                         if (result != null) 
305                                                 throw new HttpException (String.Format ("Type {0} is not unique.", typeName));
306
307                                         result = type;
308                                 } 
309                         }
310
311                         if (result == null)
312                                 throw new HttpException (String.Format ("Type {0} not found.", typeName));
313
314                         return result;
315                 }
316                 
317                 internal virtual void AddDependency (string filename)
318                 {
319                         if (dependencies == null)
320                                 dependencies = new ArrayList ();
321
322                         if (!dependencies.Contains (filename))
323                                 dependencies.Add (filename);
324                 }
325                 
326                 // Properties
327                 protected abstract string DefaultDirectiveName { get; }
328
329                 internal HttpContext Context {
330                         get { return context; }
331                 }
332
333                 internal string VirtualPath {
334                         get { return vPath; }
335                 }
336
337                 internal string PhysicalPath {
338                         get { return physPath; }
339                 }
340
341                 internal string ClassName {
342                         get { return className; }
343                 }
344
345                 internal string CodeBehind {
346                         get { return codeBehind; }
347                 }
348
349                 internal bool Debug {
350                         get { return debug; }
351                 }
352
353                 internal string Language {
354                         get { return language; }
355                 }
356
357                 internal string Program {
358                         get { return program; }
359                 }
360
361                 internal ArrayList Assemblies {
362                         get { return assemblies; }
363                 }
364
365                 internal ArrayList Dependencies {
366                         get { return dependencies; }
367                 }
368
369                 internal string PrivateBinPath {
370                         get {
371                                 if (privateBinPath != null)
372                                         return privateBinPath;
373
374                                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
375                                 privateBinPath = setup.PrivateBinPath;
376                                         
377                                 if (!Path.IsPathRooted (privateBinPath)) {
378                                         string appbase = setup.ApplicationBase;
379                                         if (appbase.StartsWith ("file://")) {
380                                                 appbase = appbase.Substring (7);
381                                                 if (Path.DirectorySeparatorChar != '/')
382                                                         appbase = appbase.Replace ('/', Path.DirectorySeparatorChar);
383                                         }
384                                         privateBinPath = Path.Combine (appbase, privateBinPath);
385                                 }
386
387                                 return privateBinPath;
388                         }
389                 }
390
391                 internal string BaseDir {
392                         get {
393                                 if (baseDir == null)
394                                         baseDir = context.Request.MapPath (BaseVirtualDir);
395
396                                 return baseDir;
397                         }
398                 }
399
400                 internal virtual string BaseVirtualDir {
401                         get {
402                                 if (baseVDir == null)
403                                         baseVDir = UrlUtils.GetDirectory (context.Request.FilePath);
404
405                                 return baseVDir;
406                         }
407                 }
408
409                 internal CompilationConfiguration CompilationConfig {
410                         get {
411                                 if (compilationConfig == null)
412                                         compilationConfig = CompilationConfiguration.GetInstance (context);
413
414                                 return compilationConfig;
415                         }
416                 }
417         }
418 }
419