Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / System.Data / System / Data / Common / DbConnectionStringCommon.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DbConnectionStringBuilder.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
8
9     using System;
10     using System.Collections;
11     using System.Collections.Generic;
12     using System.ComponentModel;
13     using System.Data;
14     using System.Data.Common;
15     using System.Diagnostics;
16     using System.Globalization;
17     using System.Runtime.Serialization;
18     using System.Security.Permissions;
19     using System.Text;
20     using System.Text.RegularExpressions;
21 using System.Data.SqlClient;
22
23 namespace System.Data.Common {
24
25 /*
26     internal sealed class NamedConnectionStringConverter : StringConverter {
27
28         public NamedConnectionStringConverter() {
29         }
30
31         public override bool GetStandardValuesSupported(ITypeDescriptorContext context) {
32             return true;
33         }
34
35         public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) {
36             // Although theoretically this could be true, some people may want to just type in a name
37             return false;
38         }
39
40         public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
41             StandardValuesCollection standardValues = null;
42             if (null != context) {
43                 DbConnectionStringBuilder instance = (context.Instance as DbConnectionStringBuilder);
44                 if (null != instance) {
45                     string myProviderName = instance.GetType().Namespace;
46
47                     List<string> myConnectionNames = new List<string>();
48                     foreach(System.Configuration.ConnectionStringSetting setting in System.Configuration.ConfigurationManager.ConnectionStrings) {
49                         if (myProviderName.EndsWith(setting.ProviderName)) {
50                             myConnectionNames.Add(setting.ConnectionName);
51                         }
52                     }
53                     standardValues = new StandardValuesCollection(myConnectionNames);
54                 }
55             }
56             return standardValues;
57         }
58     }
59 */
60
61     internal class DbConnectionStringBuilderDescriptor : PropertyDescriptor {
62         private Type _componentType;
63         private Type _propertyType;
64         private bool _isReadOnly;
65         private bool _refreshOnChange;
66
67         internal DbConnectionStringBuilderDescriptor(string propertyName, Type componentType, Type propertyType, bool isReadOnly, Attribute[] attributes) : base(propertyName, attributes) {
68             //Bid.Trace("<comm.DbConnectionStringBuilderDescriptor|INFO> propertyName='%ls', propertyType='%ls'\n", propertyName, propertyType.Name);
69             _componentType = componentType;
70             _propertyType = propertyType;
71             _isReadOnly = isReadOnly;
72         }
73
74         internal bool RefreshOnChange {
75             get {
76                 return _refreshOnChange;
77             }
78             set {
79                 _refreshOnChange = value;
80             }
81         }
82
83         public override Type ComponentType {
84             get {
85                 return _componentType;
86             }
87         }
88         public override bool IsReadOnly {
89             get {
90                 return _isReadOnly;
91             }
92         }
93         public override Type PropertyType {
94             get {
95                 return _propertyType;
96             }
97         }
98         public override bool CanResetValue(object component) {
99             DbConnectionStringBuilder builder = (component as DbConnectionStringBuilder);
100             return ((null != builder) && builder.ShouldSerialize(DisplayName));
101         }
102         public override object GetValue(object component) {
103             DbConnectionStringBuilder builder = (component as DbConnectionStringBuilder);
104             if (null != builder) {
105                 object value;
106                 if (builder.TryGetValue(DisplayName, out value)) {
107                     return value;
108                 }
109             }
110             return null;
111         }
112         public override void ResetValue(object component) {
113             DbConnectionStringBuilder builder = (component as DbConnectionStringBuilder);
114             if (null != builder) {
115                 builder.Remove(DisplayName);
116
117                 if (RefreshOnChange) {
118                     builder.ClearPropertyDescriptors();
119                 }
120             }
121         }
122         public override void SetValue(object component, object value) {
123             DbConnectionStringBuilder builder = (component as DbConnectionStringBuilder);
124             if (null != builder) {
125                 // via the editor, empty string does a defacto Reset
126                 if ((typeof(string) == PropertyType) && String.Empty.Equals(value)) {
127                     value = null;
128                 }
129                 builder[DisplayName] = value;
130
131                 if (RefreshOnChange) {
132                     builder.ClearPropertyDescriptors();
133                 }
134             }
135         }
136         public override bool ShouldSerializeValue(object component) {
137             DbConnectionStringBuilder builder = (component as DbConnectionStringBuilder);
138             return ((null != builder) && builder.ShouldSerialize(DisplayName));
139         }
140     }
141
142     [Serializable()]
143     internal sealed class ReadOnlyCollection<T> : System.Collections.ICollection, ICollection<T> {
144         private T[] _items;
145
146         internal ReadOnlyCollection(T[] items) {
147             _items = items;
148 #if DEBUG
149             for(int i = 0; i < items.Length; ++i) {
150                 Debug.Assert(null != items[i], "null item");
151             }
152 #endif
153         }
154
155         public void CopyTo(T[] array, int arrayIndex) {
156             Array.Copy(_items, 0, array, arrayIndex, _items.Length);
157         }
158
159         void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) {
160             Array.Copy(_items, 0, array, arrayIndex, _items.Length);
161         }
162
163
164         IEnumerator<T> IEnumerable<T>.GetEnumerator() {
165             return new Enumerator<T>(_items);
166         }
167
168         public System.Collections.IEnumerator GetEnumerator() {
169             return new Enumerator<T>(_items);
170         }
171
172         bool System.Collections.ICollection.IsSynchronized {
173             get { return false; }
174         }
175
176         Object System.Collections.ICollection.SyncRoot {
177             get { return _items; }
178         }
179
180         bool ICollection<T>.IsReadOnly {
181             get { return true;}
182         }
183
184         void ICollection<T>.Add(T value) {
185             throw new NotSupportedException();
186         }
187
188         void ICollection<T>.Clear() {
189             throw new NotSupportedException();
190         }
191
192         bool ICollection<T>.Contains(T value) {
193             return Array.IndexOf(_items, value) >= 0;
194         }
195
196         bool ICollection<T>.Remove(T value) {
197             throw new NotSupportedException();
198         }
199
200         public int Count {
201             get { return _items.Length; }
202         }
203
204         [Serializable()]
205         internal struct Enumerator<K> : IEnumerator<K>, System.Collections.IEnumerator { // based on List<T>.Enumerator
206             private K[] _items;
207             private int _index;
208
209             internal Enumerator(K[] items) {
210                 _items = items;
211                 _index = -1;
212             }
213
214             public void Dispose() {
215             }
216
217             public bool MoveNext() {
218                 return (++_index < _items.Length);
219             }
220
221             public K Current {
222                 get {
223                     return _items[_index];
224                 }
225             }
226
227             Object System.Collections.IEnumerator.Current {
228                 get {
229                     return _items[_index];
230                 }
231             }
232
233             void System.Collections.IEnumerator.Reset() {
234                 _index = -1;
235             }
236         }
237     }
238
239     internal static class DbConnectionStringBuilderUtil
240     {
241
242         internal static bool ConvertToBoolean(object value)
243         {
244             Debug.Assert(null != value, "ConvertToBoolean(null)");
245             string svalue = (value as string);
246             if (null != svalue)
247             {
248                 if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "true") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "yes"))
249                     return true;
250                 else if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "false") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "no"))
251                     return false;
252                 else
253                 {
254                     string tmp = svalue.Trim();  // Remove leading & trailing white space.
255                     if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "true") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "yes"))
256                         return true;
257                     else if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "false") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "no"))
258                         return false;
259                 }
260                 return Boolean.Parse(svalue);
261             }
262             try
263             {
264                 return ((IConvertible)value).ToBoolean(CultureInfo.InvariantCulture);
265             }
266             catch (InvalidCastException e)
267             {
268                 throw ADP.ConvertFailed(value.GetType(), typeof(Boolean), e);
269             }
270         }
271
272         internal static bool ConvertToIntegratedSecurity(object value)
273         {
274             Debug.Assert(null != value, "ConvertToIntegratedSecurity(null)");
275             string svalue = (value as string);
276             if (null != svalue)
277             {
278                 if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "sspi") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "true") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "yes"))
279                     return true;
280                 else if (StringComparer.OrdinalIgnoreCase.Equals(svalue, "false") || StringComparer.OrdinalIgnoreCase.Equals(svalue, "no"))
281                     return false;
282                 else
283                 {
284                     string tmp = svalue.Trim();  // Remove leading & trailing white space.
285                     if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "sspi") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "true") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "yes"))
286                         return true;
287                     else if (StringComparer.OrdinalIgnoreCase.Equals(tmp, "false") || StringComparer.OrdinalIgnoreCase.Equals(tmp, "no"))
288                         return false;
289                 }
290                 return Boolean.Parse(svalue);
291             }
292             try
293             {
294                 return ((IConvertible)value).ToBoolean(CultureInfo.InvariantCulture);
295             }
296             catch (InvalidCastException e)
297             {
298                 throw ADP.ConvertFailed(value.GetType(), typeof(Boolean), e);
299             }
300         }
301
302         internal static int ConvertToInt32(object value)
303         {
304             try
305             {
306                 return ((IConvertible)value).ToInt32(CultureInfo.InvariantCulture);
307             }
308             catch (InvalidCastException e)
309             {
310                 throw ADP.ConvertFailed(value.GetType(), typeof(Int32), e);
311             }
312         }
313
314         internal static string ConvertToString(object value)
315         {
316             try
317             {
318                 return ((IConvertible)value).ToString(CultureInfo.InvariantCulture);
319             }
320             catch (InvalidCastException e)
321             {
322                 throw ADP.ConvertFailed(value.GetType(), typeof(String), e);
323             }
324         }
325
326         #region <<PoolBlockingPeriod Utility>>
327         const string PoolBlockingPeriodAutoString = "Auto";
328         const string PoolBlockingPeriodAlwaysBlockString = "AlwaysBlock";
329         const string PoolBlockingPeriodNeverBlockString = "NeverBlock";
330
331         internal static bool TryConvertToPoolBlockingPeriod(string value, out PoolBlockingPeriod result)
332         {
333             Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed");
334             Debug.Assert(null != value, "TryConvertToPoolBlockingPeriod(null,...)");
335
336             if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodAutoString))
337             {
338                 result = PoolBlockingPeriod.Auto;
339                 return true;
340             }
341             else if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodAlwaysBlockString))
342             {
343                 result = PoolBlockingPeriod.AlwaysBlock;
344                 return true;
345             }
346             else if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodNeverBlockString))
347             {
348                 result = PoolBlockingPeriod.NeverBlock;
349                 return true;
350             }
351             else
352             {
353                 result = DbConnectionStringDefaults.PoolBlockingPeriod;
354                 return false;
355             }
356         }
357
358         internal static bool IsValidPoolBlockingPeriodValue(PoolBlockingPeriod value)
359         {
360             Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed");
361             return value == PoolBlockingPeriod.Auto || value == PoolBlockingPeriod.AlwaysBlock || value == PoolBlockingPeriod.NeverBlock;
362         }
363
364         internal static string PoolBlockingPeriodToString(PoolBlockingPeriod value)
365         {
366             Debug.Assert(IsValidPoolBlockingPeriodValue(value));
367             
368             if (value == PoolBlockingPeriod.AlwaysBlock)
369             {
370                 return PoolBlockingPeriodAlwaysBlockString;
371             }
372             if (value == PoolBlockingPeriod.NeverBlock)
373             {
374                 return PoolBlockingPeriodNeverBlockString;
375             }
376             else
377             {
378                 return PoolBlockingPeriodAutoString;
379             }
380         }
381
382         /// <summary>
383         /// This method attempts to convert the given value to a PoolBlockingPeriod enum. The algorithm is:
384         /// * if the value is from type string, it will be matched against PoolBlockingPeriod enum names only, using ordinal, case-insensitive comparer
385         /// * if the value is from type PoolBlockingPeriod, it will be used as is
386         /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum
387         /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException
388         /// 
389         /// in any case above, if the conerted value is out of valid range, the method raises ArgumentOutOfRangeException.
390         /// </summary>
391         /// <returns>PoolBlockingPeriod value in the valid range</returns>
392         internal static PoolBlockingPeriod ConvertToPoolBlockingPeriod(string keyword, object value)
393         {
394             Debug.Assert(null != value, "ConvertToPoolBlockingPeriod(null)");
395             string sValue = (value as string);
396             PoolBlockingPeriod result;
397             if (null != sValue)
398             {
399                 // We could use Enum.TryParse<PoolBlockingPeriod> here, but it accepts value combinations like
400                 // "ReadOnly, ReadWrite" which are unwelcome here
401                 // Also, Enum.TryParse is 100x slower than plain StringComparer.OrdinalIgnoreCase.Equals method.
402
403                 if (TryConvertToPoolBlockingPeriod(sValue, out result))
404                 {
405                     return result;
406                 }
407
408                 // try again after remove leading & trailing whitespaces.
409                 sValue = sValue.Trim();
410                 if (TryConvertToPoolBlockingPeriod(sValue, out result))
411                 {
412                     return result;
413                 }
414
415                 // string values must be valid
416                 throw ADP.InvalidConnectionOptionValue(keyword);
417             }
418             else
419             {
420                 // the value is not string, try other options
421                 PoolBlockingPeriod eValue;
422
423                 if (value is PoolBlockingPeriod)
424                 {
425                     // quick path for the most common case
426                     eValue = (PoolBlockingPeriod)value;
427                 }
428                 else if (value.GetType().IsEnum)
429                 {
430                     // explicitly block scenarios in which user tries to use wrong enum types, like:
431                     // builder["PoolBlockingPeriod"] = EnvironmentVariableTarget.Process;
432                     // workaround: explicitly cast non-PoolBlockingPeriod enums to int
433                     throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), null);
434                 }
435                 else
436                 {
437                     try
438                     {
439                         // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest
440                         eValue = (PoolBlockingPeriod)Enum.ToObject(typeof(PoolBlockingPeriod), value);
441                     }
442                     catch (ArgumentException e)
443                     {
444                         // to be consistent with the messages we send in case of wrong type usage, replace 
445                         // the error with our exception, and keep the original one as inner one for troubleshooting
446                         throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), e);
447                     }
448                 }
449
450                 // ensure value is in valid range
451                 if (IsValidPoolBlockingPeriodValue(eValue))
452                 {
453                     return eValue;
454                 }
455                 else
456                 {
457                     throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)eValue);
458                 }
459             }
460         }
461         #endregion
462
463         const string ApplicationIntentReadWriteString = "ReadWrite";
464         const string ApplicationIntentReadOnlyString = "ReadOnly";
465
466         internal static bool TryConvertToApplicationIntent(string value, out ApplicationIntent result)
467         {
468             Debug.Assert(Enum.GetNames(typeof(ApplicationIntent)).Length == 2, "ApplicationIntent enum has changed, update needed");
469             Debug.Assert(null != value, "TryConvertToApplicationIntent(null,...)");
470
471             if (StringComparer.OrdinalIgnoreCase.Equals(value, ApplicationIntentReadOnlyString))
472             {
473                 result = ApplicationIntent.ReadOnly;
474                 return true;
475             }
476             else if (StringComparer.OrdinalIgnoreCase.Equals(value, ApplicationIntentReadWriteString))
477             {
478                 result = ApplicationIntent.ReadWrite;
479                 return true;
480             }
481             else
482             {
483                 result = DbConnectionStringDefaults.ApplicationIntent;
484                 return false;
485             }
486         }
487
488         internal static bool IsValidApplicationIntentValue(ApplicationIntent value)
489         {
490             Debug.Assert(Enum.GetNames(typeof(ApplicationIntent)).Length == 2, "ApplicationIntent enum has changed, update needed");
491             return value == ApplicationIntent.ReadOnly || value == ApplicationIntent.ReadWrite;
492         }
493
494         internal static string ApplicationIntentToString(ApplicationIntent value)
495         {
496             Debug.Assert(IsValidApplicationIntentValue(value));
497             if (value == ApplicationIntent.ReadOnly)
498             {
499                 return ApplicationIntentReadOnlyString;
500             }
501             else
502             {
503                 return ApplicationIntentReadWriteString;
504             }
505         }
506
507         /// <summary>
508         /// This method attempts to convert the given value tp ApplicationIntent enum. The algorithm is:
509         /// * if the value is from type string, it will be matched against ApplicationIntent enum names only, using ordinal, case-insensitive comparer
510         /// * if the value is from type ApplicationIntent, it will be used as is
511         /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum
512         /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException
513         /// 
514         /// in any case above, if the conerted value is out of valid range, the method raises ArgumentOutOfRangeException.
515         /// </summary>
516         /// <returns>applicaiton intent value in the valid range</returns>
517         internal static ApplicationIntent ConvertToApplicationIntent(string keyword, object value)
518         {
519             Debug.Assert(null != value, "ConvertToApplicationIntent(null)");
520             string sValue = (value as string);
521             ApplicationIntent result;
522             if (null != sValue)
523             {
524                 // We could use Enum.TryParse<ApplicationIntent> here, but it accepts value combinations like
525                 // "ReadOnly, ReadWrite" which are unwelcome here
526                 // Also, Enum.TryParse is 100x slower than plain StringComparer.OrdinalIgnoreCase.Equals method.
527
528                 if (TryConvertToApplicationIntent(sValue, out result))
529                 {
530                     return result;
531                 }
532
533                 // try again after remove leading & trailing whitespaces.
534                 sValue = sValue.Trim();
535                 if (TryConvertToApplicationIntent(sValue, out result))
536                 {
537                     return result;
538                 }
539
540                 // string values must be valid
541                 throw ADP.InvalidConnectionOptionValue(keyword);
542             }
543             else
544             {
545                 // the value is not string, try other options
546                 ApplicationIntent eValue;
547
548                 if (value is ApplicationIntent)
549                 {
550                     // quick path for the most common case
551                     eValue = (ApplicationIntent)value;
552                 }
553                 else if (value.GetType().IsEnum)
554                 {
555                     // explicitly block scenarios in which user tries to use wrong enum types, like:
556                     // builder["ApplicationIntent"] = EnvironmentVariableTarget.Process;
557                     // workaround: explicitly cast non-ApplicationIntent enums to int
558                     throw ADP.ConvertFailed(value.GetType(), typeof(ApplicationIntent), null);
559                 }
560                 else
561                 {
562                     try
563                     {
564                         // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest
565                         eValue = (ApplicationIntent)Enum.ToObject(typeof(ApplicationIntent), value);
566                     }
567                     catch (ArgumentException e)
568                     {
569                         // to be consistent with the messages we send in case of wrong type usage, replace 
570                         // the error with our exception, and keep the original one as inner one for troubleshooting
571                         throw ADP.ConvertFailed(value.GetType(), typeof(ApplicationIntent), e);
572                     }
573                 }
574
575                 // ensure value is in valid range
576                 if (IsValidApplicationIntentValue(eValue))
577                 {
578                     return eValue;
579                 }
580                 else
581                 {
582                     throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)eValue);
583                 }
584             }
585         }
586
587         const string SqlPasswordString = "Sql Password";
588         const string ActiveDirectoryPasswordString = "Active Directory Password";
589         const string ActiveDirectoryIntegratedString = "Active Directory Integrated";
590
591         internal static bool TryConvertToAuthenticationType(string value, out SqlAuthenticationMethod result)
592         {
593             Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 4, "SqlAuthenticationMethod enum has changed, update needed");
594
595             bool isSuccess = false;
596
597             if (StringComparer.InvariantCultureIgnoreCase.Equals(value, SqlPasswordString))
598             {
599                 result = SqlAuthenticationMethod.SqlPassword;
600                 isSuccess = true;
601             }
602             else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryPasswordString))
603             {
604                 result = SqlAuthenticationMethod.ActiveDirectoryPassword;
605                 isSuccess = true;
606             }
607             else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ActiveDirectoryIntegratedString))
608             {
609                 result = SqlAuthenticationMethod.ActiveDirectoryIntegrated;
610                 isSuccess = true;
611             }
612             else
613             {
614                 result = DbConnectionStringDefaults.Authentication;
615             }
616             return isSuccess;
617         }
618
619         /// <summary>
620         /// Column Encryption Setting.
621         /// </summary>
622         const string ColumnEncryptionSettingEnabledString = "Enabled";
623         const string ColumnEncryptionSettingDisabledString = "Disabled";
624
625         /// <summary>
626         /// Convert a string value to the corresponding SqlConnectionColumnEncryptionSetting.
627         /// </summary>
628         /// <param name="value"></param>
629         /// <param name="result"></param>
630         /// <returns></returns>
631         internal static bool TryConvertToColumnEncryptionSetting(string value, out SqlConnectionColumnEncryptionSetting result) {
632             bool isSuccess = false;
633
634             if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ColumnEncryptionSettingEnabledString)) {
635                 result = SqlConnectionColumnEncryptionSetting.Enabled;
636                 isSuccess = true;
637             }
638             else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, ColumnEncryptionSettingDisabledString)) {
639                 result = SqlConnectionColumnEncryptionSetting.Disabled;
640                 isSuccess = true;
641             }
642             else {
643                 result = DbConnectionStringDefaults.ColumnEncryptionSetting;
644             }
645
646             return isSuccess;
647         }
648
649         /// <summary>
650         /// Is it a valid connection level column encryption setting ?
651         /// </summary>
652         /// <param name="value"></param>
653         /// <returns></returns>
654         internal static bool IsValidColumnEncryptionSetting(SqlConnectionColumnEncryptionSetting value) {
655             Debug.Assert(Enum.GetNames(typeof(SqlConnectionColumnEncryptionSetting)).Length == 2, "SqlConnectionColumnEncryptionSetting enum has changed, update needed");
656             return value == SqlConnectionColumnEncryptionSetting.Enabled || value == SqlConnectionColumnEncryptionSetting.Disabled;
657         }
658
659         /// <summary>
660         /// Convert connection level column encryption setting value to string.
661         /// </summary>
662         /// <param name="value"></param>
663         /// <returns></returns>
664         internal static string ColumnEncryptionSettingToString(SqlConnectionColumnEncryptionSetting value) {
665             Debug.Assert(IsValidColumnEncryptionSetting(value), "value is not a valid connection level column encryption setting.");
666
667             switch (value) {
668                 case SqlConnectionColumnEncryptionSetting.Enabled:
669                     return ColumnEncryptionSettingEnabledString;
670                 case SqlConnectionColumnEncryptionSetting.Disabled:
671                     return ColumnEncryptionSettingDisabledString;
672
673                 default:
674                     return null;
675             }
676         }
677
678         internal static bool IsValidAuthenticationTypeValue(SqlAuthenticationMethod value) {
679             Debug.Assert(Enum.GetNames(typeof(SqlAuthenticationMethod)).Length == 4, "SqlAuthenticationMethod enum has changed, update needed");
680             return value == SqlAuthenticationMethod.SqlPassword
681                 || value == SqlAuthenticationMethod.ActiveDirectoryPassword
682                 || value == SqlAuthenticationMethod.ActiveDirectoryIntegrated
683                 || value == SqlAuthenticationMethod.NotSpecified;
684         }
685
686         internal static string AuthenticationTypeToString(SqlAuthenticationMethod value)
687         {
688             Debug.Assert(IsValidAuthenticationTypeValue(value));
689
690             switch (value)
691             {
692                 case SqlAuthenticationMethod.SqlPassword:
693                     return SqlPasswordString;
694                 case SqlAuthenticationMethod.ActiveDirectoryPassword:
695                     return ActiveDirectoryPasswordString;
696                 case SqlAuthenticationMethod.ActiveDirectoryIntegrated:
697                     return ActiveDirectoryIntegratedString;
698                 default:
699                     return null;
700             }
701         }
702
703         internal static SqlAuthenticationMethod ConvertToAuthenticationType(string keyword, object value)
704         {
705             if (null == value)
706             {
707                 return DbConnectionStringDefaults.Authentication;
708             }
709
710             string sValue = (value as string);
711             SqlAuthenticationMethod result;
712             if (null != sValue)
713             {
714                 if (TryConvertToAuthenticationType(sValue, out result))
715                 {
716                     return result;
717                 }
718
719                 // try again after remove leading & trailing whitespaces.
720                 sValue = sValue.Trim();
721                 if (TryConvertToAuthenticationType(sValue, out result))
722                 {
723                     return result;
724                 }
725
726                 // string values must be valid
727                 throw ADP.InvalidConnectionOptionValue(keyword);
728             }
729             else
730             {
731                 // the value is not string, try other options
732                 SqlAuthenticationMethod eValue;
733
734                 if (value is SqlAuthenticationMethod)
735                 {
736                     // quick path for the most common case
737                     eValue = (SqlAuthenticationMethod)value;
738                 }
739                 else if (value.GetType().IsEnum)
740                 {
741                     // explicitly block scenarios in which user tries to use wrong enum types, like:
742                     // builder["ApplicationIntent"] = EnvironmentVariableTarget.Process;
743                     // workaround: explicitly cast non-ApplicationIntent enums to int
744                     throw ADP.ConvertFailed(value.GetType(), typeof(SqlAuthenticationMethod), null);
745                 }
746                 else
747                 {
748                     try
749                     {
750                         // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest
751                         eValue = (SqlAuthenticationMethod)Enum.ToObject(typeof(SqlAuthenticationMethod), value);
752                     }
753                     catch (ArgumentException e)
754                     {
755                         // to be consistent with the messages we send in case of wrong type usage, replace 
756                         // the error with our exception, and keep the original one as inner one for troubleshooting
757                         throw ADP.ConvertFailed(value.GetType(), typeof(SqlAuthenticationMethod), e);
758                     }
759                 }
760
761                 // ensure value is in valid range
762                 if (IsValidAuthenticationTypeValue(eValue))
763                 {
764                     return eValue;
765                 }
766                 else
767                 {
768                     throw ADP.InvalidEnumerationValue(typeof(SqlAuthenticationMethod), (int)eValue);
769                 }
770             }
771         }
772
773         /// <summary>
774         /// Convert the provided value to a SqlConnectionColumnEncryptionSetting.
775         /// </summary>
776         /// <param name="keyword"></param>
777         /// <param name="value"></param>
778         /// <returns></returns>
779         internal static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSetting(string keyword, object value) {
780             if (null == value) {
781                 return DbConnectionStringDefaults.ColumnEncryptionSetting;
782             }
783
784             string sValue = (value as string);
785             SqlConnectionColumnEncryptionSetting result;
786             if (null != sValue) {
787                 if (TryConvertToColumnEncryptionSetting(sValue, out result)) {
788                     return result;
789                 }
790
791                 // try again after remove leading & trailing whitespaces.
792                 sValue = sValue.Trim();
793                 if (TryConvertToColumnEncryptionSetting(sValue, out result)) {
794                     return result;
795                 }
796
797                 // string values must be valid
798                 throw ADP.InvalidConnectionOptionValue(keyword);
799             }
800             else {
801                 // the value is not string, try other options
802                 SqlConnectionColumnEncryptionSetting eValue;
803
804                 if (value is SqlConnectionColumnEncryptionSetting) {
805                     // quick path for the most common case
806                     eValue = (SqlConnectionColumnEncryptionSetting)value;
807                 }
808                 else if (value.GetType().IsEnum) {
809                     // explicitly block scenarios in which user tries to use wrong enum types, like:
810                     // builder["SqlConnectionColumnEncryptionSetting"] = EnvironmentVariableTarget.Process;
811                     // workaround: explicitly cast non-SqlConnectionColumnEncryptionSetting enums to int
812                     throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionColumnEncryptionSetting), null);
813                 }
814                 else {
815                     try {
816                         // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest
817                         eValue = (SqlConnectionColumnEncryptionSetting)Enum.ToObject(typeof(SqlConnectionColumnEncryptionSetting), value);
818                     }
819                     catch (ArgumentException e) {
820                         // to be consistent with the messages we send in case of wrong type usage, replace 
821                         // the error with our exception, and keep the original one as inner one for troubleshooting
822                         throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionColumnEncryptionSetting), e);
823                     }
824                 }
825
826                 // ensure value is in valid range
827                 if (IsValidColumnEncryptionSetting(eValue)) {
828                     return eValue;
829                 }
830                 else {
831                     throw ADP.InvalidEnumerationValue(typeof(SqlConnectionColumnEncryptionSetting), (int)eValue);
832                 }
833             }
834         }
835     }
836
837     internal static class DbConnectionStringDefaults {
838         // all
839 //        internal const string NamedConnection           = "";
840
841         // Odbc
842         internal const string Driver                    = "";
843         internal const string Dsn                       = "";
844
845         // OleDb
846         internal const bool   AdoNetPooler              = false;
847         internal const string FileName                  = "";
848         internal const int    OleDbServices             = ~(/*DBPROPVAL_OS_AGR_AFTERSESSION*/0x00000008 | /*DBPROPVAL_OS_CLIENTCURSOR*/0x00000004); // -13
849         internal const string Provider                  = "";
850
851         // OracleClient
852         internal const bool   Unicode                   = false;
853         internal const bool   OmitOracleConnectionName  = false;
854
855         // SqlClient
856         internal const ApplicationIntent ApplicationIntent   = System.Data.SqlClient.ApplicationIntent.ReadWrite;
857                 internal const string ApplicationName                = ".Net SqlClient Data Provider";
858                 internal const bool   AsynchronousProcessing         = false;
859                 internal const string AttachDBFilename               = "";
860                 internal const int    ConnectTimeout                 = 15;
861                 internal const bool   ConnectionReset                = true;
862                 internal const bool   ContextConnection              = false;
863                 internal const string CurrentLanguage                = "";
864                 internal const string DataSource                     = "";
865                 internal const bool   Encrypt                        = false;
866                 internal const bool   Enlist                         = true;
867                 internal const string FailoverPartner                = "";
868                 internal const string InitialCatalog                 = "";
869                 internal const bool   IntegratedSecurity             = false;
870                 internal const int    LoadBalanceTimeout             = 0; // default of 0 means don't use
871                 internal const bool   MultipleActiveResultSets       = false;
872                 internal const bool   MultiSubnetFailover            = false;
873                 internal const bool   TransparentNetworkIPResolution = true;
874                 internal const int    MaxPoolSize                    = 100;
875                 internal const int    MinPoolSize                    = 0;
876                 internal const string NetworkLibrary                 = "";
877                 internal const int    PacketSize                     = 8000;
878                 internal const string Password                       =  "";
879                 internal const bool   PersistSecurityInfo            = false;
880                 internal const bool   Pooling                        = true;
881                 internal const bool   TrustServerCertificate         = false;
882                 internal const string TypeSystemVersion              = "Latest";
883                 internal const string UserID                         = "";
884                 internal const bool   UserInstance                   = false;
885                 internal const bool   Replication                    = false;
886                 internal const string WorkstationID                  = "";
887                 internal const string TransactionBinding             = "Implicit Unbind";
888                 internal const int    ConnectRetryCount              = 1;
889                 internal const int    ConnectRetryInterval           = 10;
890         internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified;
891         internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled;
892         internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto;
893     }
894
895     internal static class DbConnectionOptionKeywords {
896         // Odbc
897         internal const string Driver                    = "driver";
898         internal const string Pwd                       = "pwd";
899         internal const string UID                       = "uid";
900
901         // OleDb
902         internal const string DataProvider              = "data provider";
903         internal const string ExtendedProperties        = "extended properties";
904         internal const string FileName                  = "file name";
905         internal const string Provider                  = "provider";
906         internal const string RemoteProvider            = "remote provider";
907
908         // common keywords (OleDb, OracleClient, SqlClient)
909         internal const string Password                  = "password";
910         internal const string UserID                    = "user id";
911     }
912
913     internal static class DbConnectionStringKeywords {
914         // all
915 //        internal const string NamedConnection           = "Named Connection";
916
917         // Odbc
918         internal const string Driver                    = "Driver";
919         internal const string Dsn                       = "Dsn";
920         internal const string FileDsn                   = "FileDsn";
921         internal const string SaveFile                  = "SaveFile";
922
923         // OleDb
924         internal const string FileName                  = "File Name";
925         internal const string OleDbServices             = "OLE DB Services";
926         internal const string Provider                  = "Provider";
927
928         // OracleClient
929         internal const string Unicode                   = "Unicode";
930         internal const string OmitOracleConnectionName  = "Omit Oracle Connection Name";
931
932         // SqlClient
933                 internal const string ApplicationIntent              = "ApplicationIntent";
934                 internal const string ApplicationName                = "Application Name";
935                 internal const string AsynchronousProcessing         = "Asynchronous Processing";
936                 internal const string AttachDBFilename               = "AttachDbFilename";
937                 internal const string ConnectTimeout                 = "Connect Timeout";
938                 internal const string ConnectionReset                = "Connection Reset";
939                 internal const string ContextConnection              = "Context Connection";
940                 internal const string CurrentLanguage                = "Current Language";
941                 internal const string Encrypt                        = "Encrypt";
942                 internal const string FailoverPartner                = "Failover Partner";
943                 internal const string InitialCatalog                 = "Initial Catalog";
944                 internal const string MultipleActiveResultSets       = "MultipleActiveResultSets";
945                 internal const string MultiSubnetFailover            = "MultiSubnetFailover";
946                 internal const string TransparentNetworkIPResolution = "TransparentNetworkIPResolution";
947                 internal const string NetworkLibrary                 = "Network Library";
948                 internal const string PacketSize                     = "Packet Size";
949                 internal const string Replication                    = "Replication";
950                 internal const string TransactionBinding             = "Transaction Binding";
951                 internal const string TrustServerCertificate         = "TrustServerCertificate";
952                 internal const string TypeSystemVersion              = "Type System Version";
953                 internal const string UserInstance                   = "User Instance";
954                 internal const string WorkstationID                  = "Workstation ID";
955                 internal const string ConnectRetryCount              = "ConnectRetryCount";
956                 internal const string ConnectRetryInterval           = "ConnectRetryInterval";
957                 internal const string Authentication                 = "Authentication";
958                 internal const string Certificate                    = "Certificate";
959                 internal const string ColumnEncryptionSetting        = "Column Encryption Setting";
960         internal const string PoolBlockingPeriod             = "PoolBlockingPeriod";
961         
962         // common keywords (OleDb, OracleClient, SqlClient)
963         internal const string DataSource                = "Data Source";
964         internal const string IntegratedSecurity        = "Integrated Security";
965         internal const string Password                  = "Password";
966         internal const string PersistSecurityInfo       = "Persist Security Info";
967         internal const string UserID                    = "User ID";
968
969         // managed pooling (OracleClient, SqlClient)
970         internal const string Enlist                    = "Enlist";
971         internal const string LoadBalanceTimeout        = "Load Balance Timeout";
972         internal const string MaxPoolSize               = "Max Pool Size";
973         internal const string Pooling                   = "Pooling";
974         internal const string MinPoolSize               = "Min Pool Size";
975     }
976
977     internal static class DbConnectionStringSynonyms {
978         //internal const string AsynchronousProcessing = Async;
979         internal const string Async                  = "async";
980
981         //internal const string ApplicationName        = APP;
982         internal const string APP                    = "app";
983
984         //internal const string AttachDBFilename       = EXTENDEDPROPERTIES+","+INITIALFILENAME;
985         internal const string EXTENDEDPROPERTIES     = "extended properties";
986         internal const string INITIALFILENAME        = "initial file name";
987
988         //internal const string ConnectTimeout         = CONNECTIONTIMEOUT+","+TIMEOUT;
989         internal const string CONNECTIONTIMEOUT      = "connection timeout";
990         internal const string TIMEOUT                = "timeout";
991
992         //internal const string CurrentLanguage        = LANGUAGE;
993         internal const string LANGUAGE               = "language";
994
995         //internal const string OraDataSource          = SERVER;
996         //internal const string SqlDataSource          = ADDR+","+ADDRESS+","+SERVER+","+NETWORKADDRESS;
997         internal const string ADDR                   = "addr";
998         internal const string ADDRESS                = "address";
999         internal const string SERVER                 = "server";
1000         internal const string NETWORKADDRESS         = "network address";
1001
1002         //internal const string InitialCatalog         = DATABASE;
1003         internal const string DATABASE               = "database";
1004
1005         //internal const string IntegratedSecurity     = TRUSTEDCONNECTION;
1006         internal const string TRUSTEDCONNECTION      = "trusted_connection"; // underscore introduced in everett
1007
1008         //internal const string LoadBalanceTimeout     = ConnectionLifetime;
1009         internal const string ConnectionLifetime     = "connection lifetime";
1010
1011         //internal const string NetworkLibrary         = NET+","+NETWORK;
1012         internal const string NET                    = "net";
1013         internal const string NETWORK                = "network";
1014
1015         internal const string WorkaroundOracleBug914652 = "Workaround Oracle Bug 914652";
1016
1017         //internal const string Password               = Pwd;
1018         internal const string Pwd                    = "pwd";
1019
1020         //internal const string PersistSecurityInfo    = PERSISTSECURITYINFO;
1021         internal const string PERSISTSECURITYINFO    = "persistsecurityinfo";
1022
1023         //internal const string UserID                 = UID+","+User;
1024         internal const string UID                    = "uid";
1025         internal const string User                   = "user";
1026
1027         //internal const string WorkstationID          = WSID;
1028         internal const string WSID                   = "wsid";
1029     }
1030 }