File: DBConnectionString.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (476 lines) | stat: -rw-r--r-- 23,414 bytes parent folder | download | duplicates (2)
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
//------------------------------------------------------------------------------
// <copyright file="DBConnectionString.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------

namespace System.Data.Common {

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.Text;
    using System.Text.RegularExpressions;

    [Serializable] // MDAC 83147
    internal sealed class DBConnectionString {
        // instances of this class are intended to be immutable, i.e readonly
        // used by permission classes so it is much easier to verify correctness
        // when not worried about the class being modified during execution

        private static class KEY {
            internal const string Password            = "password";
            internal const string PersistSecurityInfo = "persist security info";
            internal const string Pwd                 = "pwd";
        };

        // this class is serializable with Everett, so ugly field names can't be changed
        readonly private string        _encryptedUsersConnectionString;

        // hash of unique keys to values
        readonly private Hashtable     _parsetable;

        // a linked list of key/value and their length in _encryptedUsersConnectionString
        readonly private NameValuePair _keychain;

        // track the existence of "password" or "pwd" in the connection string
        // not used for anything anymore but must keep it set correct for V1.1 serialization
        readonly private bool          _hasPassword;

        readonly private string[] _restrictionValues;
        readonly private string   _restrictions;

        readonly private KeyRestrictionBehavior _behavior;

#pragma warning disable 169
        // this field is no longer used, hence the warning was disabled
        // however, it can not be removed or it will break serialization with V1.1
        readonly private string _encryptedActualConnectionString;
#pragma warning restore 169

        internal DBConnectionString(string value, string restrictions, KeyRestrictionBehavior behavior, Hashtable synonyms, bool useOdbcRules)
            : this(new DbConnectionOptions(value, synonyms, useOdbcRules), restrictions, behavior, synonyms, false)
        {
            // useOdbcRules is only used to parse the connection string, not to parse restrictions because values don't apply there
            // the hashtable doesn't need clone since it isn't shared with anything else
        }

        internal DBConnectionString(DbConnectionOptions connectionOptions)
            : this(connectionOptions, (string)null, KeyRestrictionBehavior.AllowOnly, (Hashtable)null, true)
        {
            // used by DBDataPermission to convert from DbConnectionOptions to DBConnectionString
            // since backward compatability requires Everett level classes
        }

        private DBConnectionString(DbConnectionOptions connectionOptions, string restrictions, KeyRestrictionBehavior behavior, Hashtable synonyms, bool mustCloneDictionary) { // used by DBDataPermission
            Debug.Assert(null != connectionOptions, "null connectionOptions");
            switch(behavior) {
            case KeyRestrictionBehavior.PreventUsage:
            case KeyRestrictionBehavior.AllowOnly:
                _behavior = behavior;
                break;
            default:
                throw ADP.InvalidKeyRestrictionBehavior(behavior);
            }

            // grab all the parsed details from DbConnectionOptions
            _encryptedUsersConnectionString = connectionOptions.UsersConnectionString(false);
            _hasPassword = connectionOptions.HasPasswordKeyword;
            _parsetable = connectionOptions.Parsetable;
            _keychain = connectionOptions._keyChain;

            // we do not want to serialize out user password unless directed so by "persist security info=true"
            // otherwise all instances of user's password will be replaced with "*"
            if (_hasPassword && !connectionOptions.HasPersistablePassword) {

                if (mustCloneDictionary) {
                    // clone the hashtable to replace user's password/pwd value with "*"
                    // we only need to clone if coming from DbConnectionOptions and password exists
                    _parsetable = (Hashtable) _parsetable.Clone();
                }

                // different than Everett in that instead of removing password/pwd from
                // the hashtable, we replace the value with '*'.  This is okay since we
                // serialize out with '*' so already knows what we do.  Better this way
                // than to treat password specially later on which causes problems.
                const string star = "*";
                if (_parsetable.ContainsKey(KEY.Password)) {
                    _parsetable[KEY.Password] = star;
                }
                if (_parsetable.ContainsKey(KEY.Pwd)) {
                    _parsetable[KEY.Pwd] = star;
                }

                // replace user's password/pwd value with "*" in the linked list and build a new string
                _keychain = connectionOptions.ReplacePasswordPwd(out _encryptedUsersConnectionString, true);
            }

            if (!ADP.IsEmpty(restrictions)) {
                _restrictionValues = ParseRestrictions(restrictions, synonyms);
                _restrictions = restrictions;
            }
        }

        private DBConnectionString(DBConnectionString connectionString, string[] restrictionValues, KeyRestrictionBehavior behavior) {
            // used by intersect for two equal connection strings with different restrictions
            _encryptedUsersConnectionString = connectionString._encryptedUsersConnectionString;
            _parsetable = connectionString._parsetable;
            _keychain = connectionString._keychain;
            _hasPassword = connectionString._hasPassword;

            _restrictionValues = restrictionValues;
            _restrictions = null;
            _behavior = behavior;

            Verify(restrictionValues);
        }

        internal KeyRestrictionBehavior Behavior {
            get { return _behavior; }
        }

        internal string ConnectionString {
            get { return _encryptedUsersConnectionString; }
        }

        internal bool IsEmpty {
            get { return (null == _keychain); }
        }

        internal NameValuePair KeyChain {
            get { return _keychain; }
        }

        internal string Restrictions {
            get {
                string restrictions = _restrictions;
                if (null == restrictions) {
                    string[] restrictionValues = _restrictionValues;
                    if ((null != restrictionValues) && (0 < restrictionValues.Length)) {
                        StringBuilder builder = new StringBuilder();
                        for(int i = 0; i < restrictionValues.Length; ++i) {
                            if (!ADP.IsEmpty(restrictionValues[i])) {
                                builder.Append(restrictionValues[i]);
                                builder.Append("=;");
                            }
#if DEBUG
                            else {
                                Debug.Assert(false, "empty restriction");
                            }
#endif
                        }
                        restrictions = builder.ToString();
                    }
                }
                return ((null != restrictions) ? restrictions: "");
            }
        }

        internal string this[string keyword] {
            get { return (string)_parsetable[keyword]; }
        }

        internal bool ContainsKey(string keyword) {
            return _parsetable.ContainsKey(keyword);
        }

        internal DBConnectionString Intersect(DBConnectionString entry) {
            KeyRestrictionBehavior behavior = _behavior;
            string[] restrictionValues = null;

            if (null == entry) {
                //Debug.WriteLine("0 entry AllowNothing");
                behavior = KeyRestrictionBehavior.AllowOnly;
            }
            else if (this._behavior != entry._behavior) { // subset of the AllowOnly array
                behavior = KeyRestrictionBehavior.AllowOnly;

                if (KeyRestrictionBehavior.AllowOnly == entry._behavior) { // this PreventUsage and entry AllowOnly
                    if (!ADP.IsEmptyArray(_restrictionValues)) {
                        if (!ADP.IsEmptyArray(entry._restrictionValues)) {
                            //Debug.WriteLine("1 this PreventUsage with restrictions and entry AllowOnly with restrictions");
                            restrictionValues = NewRestrictionAllowOnly(entry._restrictionValues, _restrictionValues);
                        }
                        else {
                            //Debug.WriteLine("2 this PreventUsage with restrictions and entry AllowOnly with no restrictions");
                        }
                    }
                    else {
                        //Debug.WriteLine("3/4 this PreventUsage with no restrictions and entry AllowOnly");
                        restrictionValues = entry._restrictionValues;
                    }
                }
                else if (!ADP.IsEmptyArray(_restrictionValues)) { // this AllowOnly and entry PreventUsage
                    if (!ADP.IsEmptyArray(entry._restrictionValues)) {
                        //Debug.WriteLine("5 this AllowOnly with restrictions and entry PreventUsage with restrictions");
                        restrictionValues = NewRestrictionAllowOnly(_restrictionValues, entry._restrictionValues);
                    }
                    else {
                        //Debug.WriteLine("6 this AllowOnly and entry PreventUsage with no restrictions");
                        restrictionValues = _restrictionValues;
                    }
                }
                else {
                    //Debug.WriteLine("7/8 this AllowOnly with no restrictions and entry PreventUsage");
                }
            }
            else if (KeyRestrictionBehavior.PreventUsage == this._behavior) { // both PreventUsage
                if (ADP.IsEmptyArray(_restrictionValues)) {
                    //Debug.WriteLine("9/10 both PreventUsage and this with no restrictions");
                    restrictionValues = entry._restrictionValues;
                }
                else if (ADP.IsEmptyArray(entry._restrictionValues)) {
                    //Debug.WriteLine("11 both PreventUsage and entry with no restrictions");
                    restrictionValues = _restrictionValues;
                }
                else {
                    //Debug.WriteLine("12 both PreventUsage with restrictions");
                    restrictionValues = NoDuplicateUnion(_restrictionValues, entry._restrictionValues);
                }
            }
            else if (!ADP.IsEmptyArray(_restrictionValues) && !ADP.IsEmptyArray(entry._restrictionValues)) { // both AllowOnly with restrictions
                if (this._restrictionValues.Length <= entry._restrictionValues.Length) {
                    //Debug.WriteLine("13a this AllowOnly with restrictions and entry AllowOnly with restrictions");
                    restrictionValues = NewRestrictionIntersect(_restrictionValues, entry._restrictionValues);
                }
                else {
                    //Debug.WriteLine("13b this AllowOnly with restrictions and entry AllowOnly with restrictions");
                    restrictionValues = NewRestrictionIntersect(entry._restrictionValues, _restrictionValues);
                }
            }
            else { // both AllowOnly
                //Debug.WriteLine("14/15/16 this AllowOnly and entry AllowOnly but no restrictions");
            }

            // verify _hasPassword & _parsetable are in sync between Everett/Whidbey
            Debug.Assert(!_hasPassword || ContainsKey(KEY.Password) || ContainsKey(KEY.Pwd), "OnDeserialized password mismatch this");
            Debug.Assert(null == entry || !entry._hasPassword || entry.ContainsKey(KEY.Password) || entry.ContainsKey(KEY.Pwd), "OnDeserialized password mismatch entry");

            DBConnectionString value = new DBConnectionString(this, restrictionValues, behavior);
            ValidateCombinedSet(this, value);
            ValidateCombinedSet(entry, value);
            
            return value;
        }

        [Conditional("DEBUG")]
        private void ValidateCombinedSet(DBConnectionString componentSet, DBConnectionString combinedSet) {
            Debug.Assert(combinedSet != null, "The combined connection string should not be null");
            if ((componentSet != null) && (combinedSet._restrictionValues != null) && (componentSet._restrictionValues != null)) {
                if (componentSet._behavior == KeyRestrictionBehavior.AllowOnly) {
                    if (combinedSet._behavior == KeyRestrictionBehavior.AllowOnly) {
                        // Component==Allow, Combined==Allow
                        // All values in the Combined Set should also be in the Component Set
                        // Combined - Component == null
                        Debug.Assert(combinedSet._restrictionValues.Except(componentSet._restrictionValues).Count() == 0, "Combined set allows values not allowed by component set");
                    }
                    else if (combinedSet._behavior == KeyRestrictionBehavior.PreventUsage) {
                        // Component==Allow, Combined==PreventUsage
                        // Preventions override allows, so there is nothing to check here
                    }
                    else {
                        Debug.Assert(false, string.Format("Unknown behavior for combined set: {0}", combinedSet._behavior));
                    }
                }
                else if (componentSet._behavior == KeyRestrictionBehavior.PreventUsage) {
                    if (combinedSet._behavior == KeyRestrictionBehavior.AllowOnly) {
                        // Component==PreventUsage, Combined==Allow
                        // There shouldn't be any of the values from the Component Set in the Combined Set
                        // Intersect(Component, Combined) == null
                        Debug.Assert(combinedSet._restrictionValues.Intersect(componentSet._restrictionValues).Count() == 0, "Combined values allows values prevented by component set");
                    }
                    else if (combinedSet._behavior == KeyRestrictionBehavior.PreventUsage) {
                        // Component==PreventUsage, Combined==PreventUsage
                        // All values in the Component Set should also be in the Combined Set
                        // Component - Combined == null
                        Debug.Assert(componentSet._restrictionValues.Except(combinedSet._restrictionValues).Count() == 0, "Combined values does not prevent all of the values prevented by the component set");
                    }
                    else {
                        Debug.Assert(false, string.Format("Unknown behavior for combined set: {0}", combinedSet._behavior));
                    }
                }
                else {
                    Debug.Assert(false, string.Format("Unknown behavior for component set: {0}", componentSet._behavior));
                }
            }
        }

        private bool IsRestrictedKeyword(string key) {
            // restricted if not found
            return ((null == _restrictionValues) || (0 > Array.BinarySearch(_restrictionValues, key, StringComparer.Ordinal)));
        }

        internal bool IsSupersetOf(DBConnectionString entry) {
            Debug.Assert(!_hasPassword || ContainsKey(KEY.Password) || ContainsKey(KEY.Pwd), "OnDeserialized password mismatch this");
            Debug.Assert(!entry._hasPassword || entry.ContainsKey(KEY.Password) || entry.ContainsKey(KEY.Pwd), "OnDeserialized password mismatch entry");

            switch(_behavior) {
            case KeyRestrictionBehavior.AllowOnly:
                // every key must either be in the resticted connection string or in the allowed keywords
                // keychain may contain duplicates, but it is better than GetEnumerator on _parsetable.Keys
                for(NameValuePair current = entry.KeyChain; null != current; current = current.Next) {
                    if (!ContainsKey(current.Name) && IsRestrictedKeyword(current.Name)) {
                        return false;
                    }
                }
                break;
            case KeyRestrictionBehavior.PreventUsage:
                // every key can not be in the restricted keywords (even if in the restricted connection string)
                if (null != _restrictionValues) {
                    foreach(string restriction in _restrictionValues) {
                        if (entry.ContainsKey(restriction)) {
                            return false;
                        }
                    }
                }
                break;
            default:
                Debug.Assert(false, "invalid KeyRestrictionBehavior");
                throw ADP.InvalidKeyRestrictionBehavior(_behavior);
            }
            return true;
        }

        static private string[] NewRestrictionAllowOnly(string[] allowonly, string[] preventusage) {
            List<string> newlist = null;
            for (int i = 0; i < allowonly.Length; ++i) {
                if (0 > Array.BinarySearch(preventusage, allowonly[i], StringComparer.Ordinal)) {
                    if (null == newlist) {
                        newlist = new List<string>();
                    }
                    newlist.Add(allowonly[i]);
                }
            }
            string[] restrictionValues = null;
            if (null != newlist) {
                restrictionValues = newlist.ToArray();
            }
            Verify(restrictionValues);
            return restrictionValues;
        }

        static private string[] NewRestrictionIntersect(string[] a, string[] b) {
            List<string> newlist = null;
            for (int i = 0; i < a.Length; ++i) {
                if (0 <= Array.BinarySearch(b, a[i], StringComparer.Ordinal)) {
                    if (null == newlist) {
                        newlist = new List<string>();
                    }
                    newlist.Add(a[i]);
                }
            }
            string[] restrictionValues = null;
            if (newlist != null) {
                restrictionValues = newlist.ToArray();
            }
            Verify(restrictionValues);
            return restrictionValues;
        }

        static private string[] NoDuplicateUnion(string[] a, string[] b) {
#if DEBUG
            Debug.Assert(null != a && 0 < a.Length, "empty a");
            Debug.Assert(null != b && 0 < b.Length, "empty b");
            Verify(a);
            Verify(b);
#endif
            List<string> newlist = new List<string>(a.Length + b.Length);
            for(int i = 0; i < a.Length; ++i) {
                newlist.Add(a[i]);
            }
            for(int i = 0; i < b.Length; ++i) { // find duplicates
                if (0 > Array.BinarySearch(a, b[i], StringComparer.Ordinal)) {
                    newlist.Add(b[i]);
                }
            }
            string[] restrictionValues = newlist.ToArray();
            Array.Sort(restrictionValues, StringComparer.Ordinal);
            Verify(restrictionValues);
            return restrictionValues;
        }

        private static string[] ParseRestrictions(string restrictions, Hashtable synonyms) {
#if DEBUG
            if (Bid.AdvancedOn) {
                Bid.Trace("<comm.DBConnectionString|INFO|ADV> Restrictions='%ls'\n", restrictions);
            }
#endif
            List<string> restrictionValues = new List<string>();
            StringBuilder buffer = new StringBuilder(restrictions.Length);

            int nextStartPosition = 0;
            int endPosition = restrictions.Length;
            while (nextStartPosition < endPosition) {
                int startPosition = nextStartPosition;

                string keyname, keyvalue; // since parsing restrictions ignores values, it doesn't matter if we use ODBC rules or OLEDB rules
                nextStartPosition = DbConnectionOptions.GetKeyValuePair(restrictions, startPosition, buffer, false, out keyname, out keyvalue);
                if (!ADP.IsEmpty(keyname)) {
#if DEBUG
                    if (Bid.AdvancedOn) {
                        Bid.Trace("<comm.DBConnectionString|INFO|ADV> KeyName='%ls'\n", keyname);
                    }
#endif
                    string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname); // MDAC 85144
                    if (ADP.IsEmpty(realkeyname)) {
                        throw ADP.KeywordNotSupported(keyname);
                    }
                    restrictionValues.Add(realkeyname);
                }
            }
            return RemoveDuplicates(restrictionValues.ToArray());

        }

        static internal string[] RemoveDuplicates(string[] restrictions) {
            int count = restrictions.Length;
            if (0 < count) {
                Array.Sort(restrictions, StringComparer.Ordinal);

                for (int i = 1; i < restrictions.Length; ++i) {
                    string prev = restrictions[i-1];
                    if ((0 == prev.Length) || (prev == restrictions[i])) {
                        restrictions[i-1] = null;
                        count--;
                    }
                }
                if (0 == restrictions[restrictions.Length-1].Length) {
                    restrictions[restrictions.Length-1] = null;
                    count--;
                }
                if (count != restrictions.Length) {
                    string[] tmp = new String[count];
                    count = 0;
                    for (int i = 0; i < restrictions.Length; ++i) {
                        if (null != restrictions[i]) {
                            tmp[count++] = restrictions[i];
                        }
                    }
                    restrictions = tmp;
                }
            }
            Verify(restrictions);
            return restrictions;
        }

        [ConditionalAttribute("DEBUG")]
        private static void Verify(string[] restrictionValues) {
            if (null != restrictionValues) {
                for (int i = 1; i < restrictionValues.Length; ++i) {
                    Debug.Assert(!ADP.IsEmpty(restrictionValues[i-1]), "empty restriction");
                    Debug.Assert(!ADP.IsEmpty(restrictionValues[i]), "empty restriction");
                    Debug.Assert(0 >= StringComparer.Ordinal.Compare(restrictionValues[i-1], restrictionValues[i]));
                }
            }
        }
    }
}