2010-01-20 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mcs / class / System.Web / System.Web.Hosting / ApplicationHost.cs
1 //
2 // System.Web.Hosting.ApplicationHost.cs 
3 // 
4 // Author:
5 //      Miguel de Icaza (miguel@novell.com)
6 //
7 //
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.Configuration;
31 using System.IO;
32 using System.Security.Permissions;
33 using System.Security.Policy;
34 using System.Text;
35 using System.Web.Configuration;
36
37 namespace System.Web.Hosting {
38
39         // CAS - no InheritanceDemand here as the class is sealed
40         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41         public sealed class ApplicationHost {
42                 internal static readonly string MonoHostedDataKey = ".:!MonoAspNetHostedApp!:.";
43                 internal static string [] WebConfigFileNames = { "web.config", "Web.config", "Web.Config" };
44
45                 ApplicationHost ()
46                 {
47                 }
48
49                 static string FindWebConfig (string basedir)
50                 {
51                         string r = null;
52                                 
53                         foreach (string s in WebConfigFileNames){
54                                 r = Path.Combine (basedir, s);
55
56                                 if (File.Exists (r))
57                                         return r;
58                         }
59                         // default: return the last one
60                         return r;
61                 }
62
63 #if NET_2_0
64                 static object create_dir = new object ();
65 #endif
66
67                 internal static bool ClearDynamicBaseDirectory (string directory)
68                 {
69                         string[] entries = null;
70                         
71                         try {
72                                 entries = Directory.GetDirectories (directory);
73                         } catch {
74                                 // ignore
75                         }
76
77                         bool dirEmpty = true;
78                         if (entries != null && entries.Length > 0) {
79                                 foreach (string e in entries) {
80                                         if (ClearDynamicBaseDirectory (e)) {
81                                                 try {
82                                                         Directory.Delete (e);
83                                                 } catch {
84                                                         dirEmpty = false;
85                                                 }
86                                         }
87                                 }
88                         }
89
90                         try {
91                                 entries = Directory.GetFiles (directory);
92                         } catch {
93                                 entries = null;
94                         }
95
96                         if (entries != null && entries.Length > 0) {
97                                 foreach (string e in entries) {
98                                         try {
99                                                 File.Delete (e);
100                                         } catch {
101                                                 dirEmpty = false;
102                                         }
103                                 }
104                         }
105
106                         return dirEmpty;
107                 }
108                 
109                 static bool CreateDirectory (string directory)
110                 {
111 #if NET_2_0
112                         lock (create_dir) {
113 #endif
114                                 if (!Directory.Exists (directory)) {
115                                         Directory.CreateDirectory (directory);
116                                         return false;
117                                 } else
118                                         return true;
119 #if NET_2_0
120                         }
121 #endif
122                 }
123
124                 static string BuildPrivateBinPath (string physicalPath, string[] dirs)
125                 {
126 #if NET_2_0
127                         int len = dirs.Length;
128                         string[] ret = new string [len];
129                         for (int i = 0; i < len; i++)
130                                 ret [i] = Path.Combine (physicalPath, dirs [i]);
131                         return String.Join (";", ret);
132 #else
133                         return String.Join (";", dirs);
134 #endif
135                 }
136                 
137                 //
138                 // For further details see `Hosting the ASP.NET runtime'
139                 //
140                 //    http://www.west-wind.com/presentations/aspnetruntime/aspnetruntime.asp
141                 // 
142 #if TARGET_JVM
143                 [MonoNotSupported ("")]
144 #endif
145                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
146                 public static object CreateApplicationHost (Type hostType, string virtualDir, string physicalDir)
147                 {
148                         if (physicalDir == null)
149                                 throw new NullReferenceException ();
150
151                         // Make sure physicalDir has file system semantics
152                         // and not uri semantics ( '\' and not '/' ).
153                         physicalDir = Path.GetFullPath (physicalDir);
154
155                         if (hostType == null)
156                                 throw new ArgumentException ("hostType can't be null");
157
158                         if (virtualDir == null)
159                                 throw new ArgumentNullException ("virtualDir");
160
161                         Evidence evidence = new Evidence (AppDomain.CurrentDomain.Evidence);
162                         
163                         //
164                         // Setup
165                         //
166                         AppDomainSetup setup = new AppDomainSetup ();
167
168                         setup.ApplicationBase = physicalDir;
169
170                         setup.ConfigurationFile = FindWebConfig (physicalDir);
171                         setup.DisallowCodeDownload = true;
172
173                         string[] bindirPath = new string [1] {
174 #if NET_2_0
175                                 Path.Combine (physicalDir, "bin")
176 #else
177                                 "bin"
178 #endif
179                         };
180                         string bindir;
181
182                         foreach (string dir in HttpApplication.BinDirs) {
183                                 bindir = Path.Combine (physicalDir, dir);
184                         
185                                 if (Directory.Exists (bindir)) {
186 #if NET_2_0
187                                         bindirPath [0] = bindir;
188 #else
189                                         bindirPath [0] = dir;
190 #endif
191                                         break;
192                                 }
193                         }
194
195                         setup.PrivateBinPath = BuildPrivateBinPath (physicalDir, bindirPath);
196                         setup.PrivateBinPathProbe = "*";
197                         string dynamic_dir = null;
198                         string user = Environment.UserName;
199                         int tempDirTag = 0;
200                         string dirPrefix = String.Concat (user, "-temp-aspnet-");
201                         
202                         for (int i = 0; ; i++){
203                                 string d = Path.Combine (Path.GetTempPath (), String.Concat (dirPrefix, i.ToString ("x")));
204                         
205                                 try {
206                                         CreateDirectory (d);
207                                         string stamp = Path.Combine (d, "stamp");
208                                         CreateDirectory (stamp);
209                                         dynamic_dir = d;
210                                         try {
211                                                 Directory.Delete (stamp);
212                                         } catch (Exception) {
213                                                 // ignore
214                                         }
215                                         
216                                         tempDirTag = i.GetHashCode ();
217                                         break;
218                                 } catch (UnauthorizedAccessException){
219                                         continue;
220                                 }
221                         }
222                         // 
223                         // Unique Domain ID
224                         //
225                         string domain_id = (virtualDir.GetHashCode () + 1 ^ physicalDir.GetHashCode () + 2 ^ tempDirTag).ToString ("x");
226
227                         // This is used by mod_mono's fail-over support
228                         string domain_id_suffix = Environment.GetEnvironmentVariable ("__MONO_DOMAIN_ID_SUFFIX");
229                         if (domain_id_suffix != null && domain_id_suffix.Length > 0)
230                                 domain_id += domain_id_suffix;
231                         
232                         setup.ApplicationName = domain_id;
233                         setup.DynamicBase = dynamic_dir;
234                         setup.CachePath = dynamic_dir;
235
236                         string dynamic_base = setup.DynamicBase;
237                         if (CreateDirectory (dynamic_base) && (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") == null))
238                                 ClearDynamicBaseDirectory (dynamic_base);
239
240                         //
241                         // Create app domain
242                         //
243                         AppDomain appdomain;
244                         appdomain = AppDomain.CreateDomain (domain_id, evidence, setup);
245
246                         //
247                         // Populate with the AppDomain data keys expected, Mono only uses a
248                         // few, but third party apps might use others:
249                         //
250                         appdomain.SetData (".appDomain", "*");
251                         int l = physicalDir.Length;
252                         if (physicalDir [l - 1] != Path.DirectorySeparatorChar)
253                                 physicalDir += Path.DirectorySeparatorChar;
254                         appdomain.SetData (".appPath", physicalDir);
255                         appdomain.SetData (".appVPath", virtualDir);
256                         appdomain.SetData (".domainId", domain_id);
257                         appdomain.SetData (".hostingVirtualPath", virtualDir);
258                         appdomain.SetData (".hostingInstallDir", Path.GetDirectoryName (typeof (Object).Assembly.CodeBase));
259 #if NET_2_0
260                         appdomain.SetData ("DataDirectory", Path.Combine (physicalDir, "App_Data"));
261                         HostingEnvironment.SetIsHosted (false);
262 #endif
263                         appdomain.SetData (MonoHostedDataKey, "yes");
264                         
265 #if NET_2_0
266                         appdomain.DoCallBack (SetHostingEnvironment);
267 #endif
268                         return appdomain.CreateInstanceAndUnwrap (hostType.Module.Assembly.FullName, hostType.FullName);
269                 }
270
271 #if NET_2_0
272                 static void SetHostingEnvironment ()
273                 {
274                         bool shadow_copy_enabled = true;
275                         HostingEnvironmentSection he = WebConfigurationManager.GetWebApplicationSection ("system.web/hostingEnvironment") as HostingEnvironmentSection;
276                         if (he != null)
277                                 shadow_copy_enabled = he.ShadowCopyBinAssemblies;
278
279                         if (shadow_copy_enabled) {
280                                 AppDomain current = AppDomain.CurrentDomain;
281                                 current.SetShadowCopyFiles ();
282                                 current.SetShadowCopyPath (current.SetupInformation.PrivateBinPath);
283                         }
284                 }
285 #endif
286         }
287 }