1 //------------------------------------------------------------------------------
2 // <copyright file="DynamicVirtualDiscoSearcher.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System
.Web
.Services
.Discovery
{
10 using System
.Collections
;
11 using System
.Diagnostics
;
13 using System
.DirectoryServices
;
14 using System
.ComponentModel
;
15 using System
.Globalization
;
16 using System
.Threading
;
17 using System
.Web
.Services
.Diagnostics
;
19 /// <include file='doc\DynamicVirtualDiscoSearcher.uex' path='docs/doc[@for="DynamicVirtualDiscoSearcher"]/*' />
21 /// Does a recursive search of virtual subdirectories to find stuff to
22 /// make a disco file from. *.disco files (or whatever the PrimarySearchPattern is) are
23 /// treated as end-points - recursion stops where they are found.
25 internal class DynamicVirtualDiscoSearcher
: DynamicDiscoSearcher
{
27 private string rootPathAsdi
; // ADSI search root path with prefix
28 private string entryPathPrefix
;
30 private string startDir
;
32 // If we could get an event back from IIS Admin Object that some directory is addred/removed/renamed
33 // then the following memeber should become static, so we get 5-10 _times_ performace gain on
34 // processing .vsdisco in the Web Root
35 // !!SEE ALSO!! CleanupCache method
36 private /*static*/ Hashtable webApps
= new Hashtable();
37 private Hashtable Adsi
= new Hashtable();
40 // -------------------------------------------------------------------------------
41 internal DynamicVirtualDiscoSearcher(string startDir
, string[] excludedUrls
, string rootUrl
) :
45 entryPathPrefix
= GetWebServerForUrl( rootUrl
) + "/ROOT";
47 this.startDir
= startDir
;
49 string localPath
= (new System
.Uri(rootUrl
)).LocalPath
;
50 if ( localPath
.Equals("/") ) localPath
= ""; // empty local path should be ""
51 rootPathAsdi
= entryPathPrefix
+ localPath
;
54 // -------------------------------------------------------------------------------
55 /// <include file='doc\DynamicVirtualDiscoSearcher.uex' path='docs/doc[@for="DynamicVirtualDiscoSearcher.Search"]/*' />
57 /// Main function. Searches dir recursively for primary (.vsdisco) and seconary (.asmx) files.
59 internal override void Search(string fileToSkipAtBegin
) {
60 SearchInit(fileToSkipAtBegin
);
61 ScanDirectory( rootPathAsdi
);
65 // -------------------------------------------------------------------------------
66 // Look in virtual subdirectories.
67 protected override void SearchSubDirectories(string nameAdsiDir
) {
69 if ( CompModSwitches
.DynamicDiscoverySearcher
.TraceVerbose
) Debug
.WriteLine( "DynamicVirtualDiscoSearcher.SearchSubDirectories(): nameAdsiDir=" + nameAdsiDir
);
71 DirectoryEntry vdir
= (DirectoryEntry
)Adsi
[nameAdsiDir
]; //may be already bound
73 if ( !DirectoryEntry
.Exists(nameAdsiDir
) )
75 vdir
= new DirectoryEntry(nameAdsiDir
);
76 Adsi
[nameAdsiDir
] = vdir
;
79 foreach (DirectoryEntry obj
in vdir
.Children
) {
80 DirectoryEntry child
= (DirectoryEntry
)Adsi
[obj
.Path
];
87 AppSettings settings
= GetAppSettings(child
);
88 if (settings
!= null) {
89 ScanDirectory(child
.Path
); //go down ADSI path
95 // -------------------------------------------------------------------------------
96 protected override DirectoryInfo
GetPhysicalDir(string dir
) {
97 DirectoryEntry vdir
= (DirectoryEntry
)Adsi
[dir
];
99 if (!DirectoryEntry
.Exists(dir
) ) {
102 vdir
= new DirectoryEntry(dir
);
106 DirectoryInfo directory
= null;
107 AppSettings settings
= GetAppSettings(vdir
);
108 if (settings
== null) {
111 if (settings
.VPath
== null) { //SchemaClassName == "IIsWebDirectory"
112 //NOTE This assumes there was a known physical directory
113 //corresponding to a parent WebDirectory.
114 //And incoming 'dir' is a child of that parent.
115 if ( !dir
.StartsWith(rootPathAsdi
, StringComparison
.Ordinal
) ) {
116 throw new ArgumentException(Res
.GetString(Res
.WebVirtualDisoRoot
, dir
, rootPathAsdi
), "dir");
118 string physicalDir
= dir
.Substring(rootPathAsdi
.Length
);
119 physicalDir
= physicalDir
.Replace('/', '\\'); //it always begins with '/' or is empty
120 directory
= new DirectoryInfo(startDir
+ physicalDir
);
124 directory
= new DirectoryInfo(settings
.VPath
); //SchemaClassName == "IIsWebVirtualDir
127 if ( directory
.Exists
)
130 catch (Exception e
) {
131 if (e
is ThreadAbortException
|| e
is StackOverflowException
|| e
is OutOfMemoryException
) {
134 if ( CompModSwitches
.DynamicDiscoverySearcher
.TraceVerbose
) Debug
.WriteLine( "+++ DynamicVirtualDiscoSearcher.GetPhysicalDir(): dir=" + dir
+ " Exception=" + e
.ToString() );
135 if (Tracing
.On
) Tracing
.ExceptionCatch(TraceEventType
.Warning
, this, "GetPhysicalDir", e
);
142 // -------------------------------------------------------------------------------
143 // Calculate root ADSI virtual directory name (func by 'Microsoft').
144 private string GetWebServerForUrl(string url
) {
145 Uri uri
= new Uri(url
);
146 DirectoryEntry w3Service
= new DirectoryEntry("IIS://" + uri
.Host
+ "/W3SVC");
148 foreach (DirectoryEntry obj
in w3Service
.Children
) {
149 DirectoryEntry site
= (DirectoryEntry
)Adsi
[obj
.Path
]; //may be already bound
152 Adsi
[obj
.Path
] = obj
;
157 AppSettings settings
= GetAppSettings(site
);
159 if (settings
== null || settings
.Bindings
== null) { //SchemaClassName != "IIsWebServer"
163 foreach (string bindingsEntry
in settings
.Bindings
) {
164 if ( CompModSwitches
.DynamicDiscoverySearcher
.TraceVerbose
) Debug
.WriteLine("GetWebServerForUrl() bindingsEntry=" + bindingsEntry
);
165 string[] bindings
= bindingsEntry
.Split(':');
166 string ip
= bindings
[0];
167 string port
= bindings
[1];
168 string hostname
= bindings
[2];
170 if (Convert
.ToInt32(port
, CultureInfo
.InvariantCulture
) != uri
.Port
)
173 if (uri
.HostNameType
== UriHostNameType
.Dns
) {
174 if (hostname
.Length
== 0 || string.Compare(hostname
, uri
.Host
, StringComparison
.OrdinalIgnoreCase
) == 0)
178 if (ip
.Length
== 0 || string.Compare(ip
, uri
.Host
, StringComparison
.OrdinalIgnoreCase
) == 0)
186 // -------------------------------------------------------------------------------
187 // Makes result URL found file path from diectory name and short file name.
188 protected override string MakeResultPath(string dirName
, string fileName
) {
190 + dirName
.Substring(rootPathAsdi
.Length
, dirName
.Length
- rootPathAsdi
.Length
)
195 // -------------------------------------------------------------------------------
196 // Makes exclusion path absolute for quick comparision on search.
197 protected override string MakeAbsExcludedPath(string pathRelativ
) {
198 return rootPathAsdi
+ '/' + pathRelativ
.Replace('\\', '/');
201 // -------------------------------------------------------------------------------
202 protected override bool IsVirtualSearch
{
206 private AppSettings
GetAppSettings(DirectoryEntry entry
) {
207 string key
= entry
.Path
; //this is fast since does not cause bind()
208 AppSettings result
= null;
210 object obj
= webApps
[key
];
213 // We provie a write lock while Hashtable supports multiple readers under single writer
216 if (obj
== null) { //make sure other thread not taken care of
217 result
= new AppSettings(entry
); //that consumes a 50-2000 ms
218 webApps
[key
] = result
;
223 result
= (AppSettings
)obj
;
225 return result
.AccessRead
? result
: null; //ignore denied object on upper level
228 private void CleanupCache() {
229 //Destroy system resources excplicitly since the destructor is called sometime late
230 foreach (DictionaryEntry obj
in Adsi
) {
231 ((DirectoryEntry
)(obj
.Value
)).Dispose();
234 entryPathPrefix
= null;
238 //REMOVE NEXT LINE IF the member webApps has turned into static (see webApps declaration line)
242 private class AppSettings
{
243 internal readonly bool AccessRead
= false; // if false the caller will ignore the object
244 internal readonly string[] Bindings
= null; // the field is only for WebServers
245 internal readonly string VPath
= null; // the filed is only for VirtualDirs
247 internal AppSettings(DirectoryEntry entry
) {
249 string schema
= entry
.SchemaClassName
;
252 if (schema
== "IIsWebVirtualDir" || schema
== "IIsWebDirectory") {
253 if (!(bool)(entry
.Properties
["AccessRead"][0])) {
257 if (schema
== "IIsWebVirtualDir") {
258 VPath
= (string) (entry
.Properties
["Path"][0]);
261 else if (schema
== "IIsWebServer") {
262 Bindings
= new string[entry
.Properties
["ServerBindings"].Count
];
263 for (int i
= 0; i
< Bindings
.Length
; ++i
) {
264 Bindings
[i
] = (string) (entry
.Properties
["ServerBindings"][i
]);
268 //schema is not recognized add to the cache but never look at the object