File: DynamicVirtualDiscoSearcher.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (275 lines) | stat: -rw-r--r-- 12,290 bytes parent folder | download | duplicates (6)
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;
                }
            }
        }

    }
}