1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
|
//------------------------------------------------------------------------------
// <copyright file="DynamicVirtualDiscoSearcher.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Services.Discovery {
using System;
using System.IO;
using System.Collections;
using System.Diagnostics;
using System.Text;
using System.DirectoryServices;
using System.ComponentModel;
using System.Globalization;
using System.Threading;
using System.Web.Services.Diagnostics;
/// <include file='doc\DynamicVirtualDiscoSearcher.uex' path='docs/doc[@for="DynamicVirtualDiscoSearcher"]/*' />
/// <devdoc>
/// Does a recursive search of virtual subdirectories to find stuff to
/// make a disco file from. *.disco files (or whatever the PrimarySearchPattern is) are
/// treated as end-points - recursion stops where they are found.
/// </devdoc>
internal class DynamicVirtualDiscoSearcher : DynamicDiscoSearcher {
private string rootPathAsdi; // ADSI search root path with prefix
private string entryPathPrefix;
private string startDir;
// If we could get an event back from IIS Admin Object that some directory is addred/removed/renamed
// then the following memeber should become static, so we get 5-10 _times_ performace gain on
// processing .vsdisco in the Web Root
// !!SEE ALSO!! CleanupCache method
private /*static*/ Hashtable webApps = new Hashtable();
private Hashtable Adsi = new Hashtable();
// -------------------------------------------------------------------------------
internal DynamicVirtualDiscoSearcher(string startDir, string[] excludedUrls, string rootUrl) :
base(excludedUrls)
{
origUrl = rootUrl;
entryPathPrefix = GetWebServerForUrl( rootUrl ) + "/ROOT";
this.startDir = startDir;
string localPath = (new System.Uri(rootUrl)).LocalPath;
if ( localPath.Equals("/") ) localPath = ""; // empty local path should be ""
rootPathAsdi = entryPathPrefix + localPath;
}
// -------------------------------------------------------------------------------
/// <include file='doc\DynamicVirtualDiscoSearcher.uex' path='docs/doc[@for="DynamicVirtualDiscoSearcher.Search"]/*' />
/// <devdoc>
/// Main function. Searches dir recursively for primary (.vsdisco) and seconary (.asmx) files.
/// </devdoc>
internal override void Search(string fileToSkipAtBegin) {
SearchInit(fileToSkipAtBegin);
ScanDirectory( rootPathAsdi );
CleanupCache();
}
// -------------------------------------------------------------------------------
// Look in virtual subdirectories.
protected override void SearchSubDirectories(string nameAdsiDir) {
if ( CompModSwitches.DynamicDiscoverySearcher.TraceVerbose ) Debug.WriteLine( "DynamicVirtualDiscoSearcher.SearchSubDirectories(): nameAdsiDir=" + nameAdsiDir);
DirectoryEntry vdir = (DirectoryEntry)Adsi[nameAdsiDir]; //may be already bound
if (vdir == null) {
if ( !DirectoryEntry.Exists(nameAdsiDir) )
return;
vdir = new DirectoryEntry(nameAdsiDir);
Adsi[nameAdsiDir] = vdir;
}
foreach (DirectoryEntry obj in vdir.Children) {
DirectoryEntry child = (DirectoryEntry)Adsi[obj.Path];
if (child == null) {
child = obj;
Adsi[obj.Path] = obj;
} else {
obj.Dispose();
}
AppSettings settings = GetAppSettings(child);
if (settings != null) {
ScanDirectory(child.Path); //go down ADSI path
}
}
}
// -------------------------------------------------------------------------------
protected override DirectoryInfo GetPhysicalDir(string dir ) {
DirectoryEntry vdir = (DirectoryEntry)Adsi[dir];
if (vdir == null) {
if (!DirectoryEntry.Exists(dir) ) {
return null;
}
vdir = new DirectoryEntry(dir);
Adsi[dir] = vdir;
}
try {
DirectoryInfo directory = null;
AppSettings settings = GetAppSettings(vdir);
if (settings == null) {
return null;
}
if (settings.VPath == null) { //SchemaClassName == "IIsWebDirectory"
//NOTE This assumes there was a known physical directory
//corresponding to a parent WebDirectory.
//And incoming 'dir' is a child of that parent.
if ( !dir.StartsWith(rootPathAsdi, StringComparison.Ordinal) ) {
throw new ArgumentException(Res.GetString(Res.WebVirtualDisoRoot, dir, rootPathAsdi), "dir");
}
string physicalDir = dir.Substring(rootPathAsdi.Length);
physicalDir = physicalDir.Replace('/', '\\'); //it always begins with '/' or is empty
directory = new DirectoryInfo(startDir + physicalDir);
}
else {
directory = new DirectoryInfo(settings.VPath); //SchemaClassName == "IIsWebVirtualDir
}
if ( directory.Exists )
return directory;
}
catch (Exception e) {
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
throw;
}
if ( CompModSwitches.DynamicDiscoverySearcher.TraceVerbose ) Debug.WriteLine( "+++ DynamicVirtualDiscoSearcher.GetPhysicalDir(): dir=" + dir + " Exception=" + e.ToString() );
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "GetPhysicalDir", e);
return null;
}
return null;
}
// -------------------------------------------------------------------------------
// Calculate root ADSI virtual directory name (func by 'Microsoft').
private string GetWebServerForUrl(string url) {
Uri uri = new Uri(url);
DirectoryEntry w3Service = new DirectoryEntry("IIS://" + uri.Host + "/W3SVC");
foreach (DirectoryEntry obj in w3Service.Children) {
DirectoryEntry site = (DirectoryEntry)Adsi[obj.Path]; //may be already bound
if (site == null) {
site = obj;
Adsi[obj.Path] = obj;
}
else {
obj.Dispose();
}
AppSettings settings = GetAppSettings(site);
if (settings == null || settings.Bindings == null) { //SchemaClassName != "IIsWebServer"
continue;
}
foreach (string bindingsEntry in settings.Bindings) {
if ( CompModSwitches.DynamicDiscoverySearcher.TraceVerbose ) Debug.WriteLine("GetWebServerForUrl() bindingsEntry=" + bindingsEntry);
string[] bindings = bindingsEntry.Split(':');
string ip = bindings[0];
string port = bindings[1];
string hostname = bindings[2];
if (Convert.ToInt32(port, CultureInfo.InvariantCulture) != uri.Port)
continue;
if (uri.HostNameType == UriHostNameType.Dns) {
if (hostname.Length == 0 || string.Compare(hostname, uri.Host, StringComparison.OrdinalIgnoreCase) == 0)
return site.Path;
}
else {
if (ip.Length == 0 || string.Compare(ip, uri.Host, StringComparison.OrdinalIgnoreCase) == 0)
return site.Path;
}
}
}
return null;
}
// -------------------------------------------------------------------------------
// Makes result URL found file path from diectory name and short file name.
protected override string MakeResultPath(string dirName, string fileName) {
string res = origUrl
+ dirName.Substring(rootPathAsdi.Length, dirName.Length - rootPathAsdi.Length)
+ '/' + fileName;
return res;
}
// -------------------------------------------------------------------------------
// Makes exclusion path absolute for quick comparision on search.
protected override string MakeAbsExcludedPath(string pathRelativ) {
return rootPathAsdi + '/' + pathRelativ.Replace('\\', '/');
}
// -------------------------------------------------------------------------------
protected override bool IsVirtualSearch {
get { return true; }
}
private AppSettings GetAppSettings(DirectoryEntry entry) {
string key = entry.Path; //this is fast since does not cause bind()
AppSettings result = null;
object obj = webApps[key];
if (obj == null) {
// We provie a write lock while Hashtable supports multiple readers under single writer
lock (webApps) {
obj = webApps[key];
if (obj == null) { //make sure other thread not taken care of
result = new AppSettings(entry); //that consumes a 50-2000 ms
webApps[key] = result;
}
}
}
else {
result = (AppSettings)obj;
}
return result.AccessRead ? result : null; //ignore denied object on upper level
}
private void CleanupCache() {
//Destroy system resources excplicitly since the destructor is called sometime late
foreach (DictionaryEntry obj in Adsi) {
((DirectoryEntry)(obj.Value)).Dispose();
}
rootPathAsdi = null;
entryPathPrefix = null;
startDir = null;
Adsi = null;
//REMOVE NEXT LINE IF the member webApps has turned into static (see webApps declaration line)
webApps = null;
}
private class AppSettings {
internal readonly bool AccessRead = false; // if false the caller will ignore the object
internal readonly string[] Bindings = null; // the field is only for WebServers
internal readonly string VPath = null; // the filed is only for VirtualDirs
internal AppSettings(DirectoryEntry entry) {
string schema = entry.SchemaClassName;
AccessRead = true;
if (schema == "IIsWebVirtualDir" || schema == "IIsWebDirectory") {
if (!(bool)(entry.Properties["AccessRead"][0])) {
AccessRead = false;
return;
}
if (schema == "IIsWebVirtualDir") {
VPath = (string) (entry.Properties["Path"][0]);
}
}
else if (schema == "IIsWebServer") {
Bindings = new string[entry.Properties["ServerBindings"].Count];
for (int i = 0; i < Bindings.Length; ++i) {
Bindings[i] = (string) (entry.Properties["ServerBindings"][i]);
}
}
else {
//schema is not recognized add to the cache but never look at the object
AccessRead = false;
}
}
}
}
}
|