| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973 |
- //------------------------------------------------------------------------------
- // <copyright file="DBConnectionOptions.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.Data;
- using System.Diagnostics;
- using System.Globalization;
- using System.Runtime.Serialization;
- using System.Security.Permissions;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Runtime.Versioning;
- internal class DbConnectionOptions {
- // instances of this class are intended to be immutable, i.e readonly
- // used by pooling classes so it is much easier to verify correctness
- // when not worried about the class being modified during execution
- #if DEBUG
- /*private const string ConnectionStringPatternV1 =
- "[\\s;]*"
- +"(?<key>([^=\\s]|\\s+[^=\\s]|\\s+==|==)+)"
- + "\\s*=(?!=)\\s*"
- +"(?<value>("
- + "(" + "\"" + "([^\"]|\"\")*" + "\"" + ")"
- + "|"
- + "(" + "'" + "([^']|'')*" + "'" + ")"
- + "|"
- + "(" + "(?![\"'])" + "([^\\s;]|\\s+[^\\s;])*" + "(?<![\"'])" + ")"
- + "))"
- + "[\\s;]*"
- ;*/
- private const string ConnectionStringPattern = // may not contain embedded null except trailing last value
- "([\\s;]*" // leading whitespace and extra semicolons
- + "(?![\\s;])" // key does not start with space or semicolon
- + "(?<key>([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}]|\\s+==|==)+)" // allow any visible character for keyname except '=' which must quoted as '=='
- + "\\s*=(?!=)\\s*" // the equal sign divides the key and value parts
- + "(?<value>"
- + "(\"([^\"\u0000]|\"\")*\")" // double quoted string, " must be quoted as ""
- + "|"
- + "('([^'\u0000]|'')*')" // single quoted string, ' must be quoted as ''
- + "|"
- + "((?![\"'\\s])" // unquoted value must not start with " or ' or space, would also like = but too late to change
- + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted
- + "(?<![\"']))" // unquoted value must not stop with " or '
- + ")(\\s*)(;|[\u0000\\s]*$)" // whitespace after value up to semicolon or end-of-line
- + ")*" // repeat the key-value pair
- + "[\\s;]*[\u0000\\s]*" // traling whitespace/semicolons (DataSourceLocator), embedded nulls are allowed only in the end
- ;
- private const string ConnectionStringPatternOdbc = // may not contain embedded null except trailing last value
- "([\\s;]*" // leading whitespace and extra semicolons
- + "(?![\\s;])" // key does not start with space or semicolon
- + "(?<key>([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}])+)" // allow any visible character for keyname except '='
- + "\\s*=\\s*" // the equal sign divides the key and value parts
- + "(?<value>"
- + "(\\{([^\\}\u0000]|\\}\\})*\\})" // quoted string, starts with { and ends with }
- + "|"
- + "((?![\\{\\s])" // unquoted value must not start with { or space, would also like = but too late to change
- + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" // control characters must be quoted
- + ")" // VSTFDEVDIV 94761: although the spec does not allow {}
- // embedded within a value, the retail code does.
- // + "(?<![\\}]))" // unquoted value must not stop with }
- + ")(\\s*)(;|[\u0000\\s]*$)" // whitespace after value up to semicolon or end-of-line
- + ")*" // repeat the key-value pair
- + "[\\s;]*[\u0000\\s]*" // traling whitespace/semicolons (DataSourceLocator), embedded nulls are allowed only in the end
- ;
- private static readonly Regex ConnectionStringRegex = new Regex(ConnectionStringPattern, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
- private static readonly Regex ConnectionStringRegexOdbc = new Regex(ConnectionStringPatternOdbc, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
- #endif
- private const string ConnectionStringValidKeyPattern = "^(?![;\\s])[^\\p{Cc}]+(?<!\\s)$"; // key not allowed to start with semi-colon or space or contain non-visible characters or end with space
- private const string ConnectionStringValidValuePattern = "^[^\u0000]*$"; // value not allowed to contain embedded null
- private const string ConnectionStringQuoteValuePattern = "^[^\"'=;\\s\\p{Cc}]*$"; // generally do not quote the value if it matches the pattern
- private const string ConnectionStringQuoteOdbcValuePattern = "^\\{([^\\}\u0000]|\\}\\})*\\}$"; // do not quote odbc value if it matches this pattern
- internal const string DataDirectory = "|datadirectory|";
- private static readonly Regex ConnectionStringValidKeyRegex = new Regex(ConnectionStringValidKeyPattern, RegexOptions.Compiled);
- private static readonly Regex ConnectionStringValidValueRegex = new Regex(ConnectionStringValidValuePattern, RegexOptions.Compiled);
- private static readonly Regex ConnectionStringQuoteValueRegex = new Regex(ConnectionStringQuoteValuePattern, RegexOptions.Compiled);
- private static readonly Regex ConnectionStringQuoteOdbcValueRegex = new Regex(ConnectionStringQuoteOdbcValuePattern, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
- // connection string common keywords
- private static class KEY {
- internal const string Integrated_Security = "integrated security";
- internal const string Password = "password";
- internal const string Persist_Security_Info = "persist security info";
- internal const string User_ID = "user id";
- };
- // known connection string common synonyms
- private static class SYNONYM {
- internal const string Pwd = "pwd";
- internal const string UID = "uid";
- };
- private readonly string _usersConnectionString;
- private readonly Hashtable _parsetable;
- internal readonly NameValuePair _keyChain;
- internal readonly bool HasPasswordKeyword;
- internal readonly bool HasUserIdKeyword;
- // differences between OleDb and Odbc
- // ODBC:
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcsqldriverconnect.asp
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbcsql/od_odbc_d_4x4k.asp
- // do not support == -> = in keywords
- // first key-value pair wins
- // quote values using \{ and \}, only driver= and pwd= appear to generically allow quoting
- // do not strip quotes from value, or add quotes except for driver keyword
- // OLEDB:
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbconnectionstringsyntax.asp
- // support == -> = in keywords
- // last key-value pair wins
- // quote values using \" or \'
- // strip quotes from value
- internal readonly bool UseOdbcRules;
- private System.Security.PermissionSet _permissionset;
- // called by derived classes that may cache based on connectionString
- public DbConnectionOptions(string connectionString)
- : this(connectionString, null, false) {
- }
- // synonyms hashtable is meant to be read-only translation of parsed string
- // keywords/synonyms to a known keyword string
- public DbConnectionOptions(string connectionString, Hashtable synonyms, bool useOdbcRules) {
- UseOdbcRules = useOdbcRules;
- _parsetable = new Hashtable();
- _usersConnectionString = ((null != connectionString) ? connectionString : "");
- // first pass on parsing, initial syntax check
- if (0 < _usersConnectionString.Length) {
- _keyChain = ParseInternal(_parsetable, _usersConnectionString, true, synonyms, UseOdbcRules);
- HasPasswordKeyword = (_parsetable.ContainsKey(KEY.Password) || _parsetable.ContainsKey(SYNONYM.Pwd));
- HasUserIdKeyword = (_parsetable.ContainsKey(KEY.User_ID) || _parsetable.ContainsKey(SYNONYM.UID));
- }
- }
- protected DbConnectionOptions(DbConnectionOptions connectionOptions) { // Clone used by SqlConnectionString
- _usersConnectionString = connectionOptions._usersConnectionString;
- HasPasswordKeyword = connectionOptions.HasPasswordKeyword;
- HasUserIdKeyword = connectionOptions.HasUserIdKeyword;
- UseOdbcRules = connectionOptions.UseOdbcRules;
- _parsetable = connectionOptions._parsetable;
- _keyChain = connectionOptions._keyChain;
- }
- public string UsersConnectionString(bool hidePassword) {
- return UsersConnectionString(hidePassword, false);
- }
- private string UsersConnectionString(bool hidePassword, bool forceHidePassword) {
- string connectionString = _usersConnectionString;
- if (HasPasswordKeyword && (forceHidePassword || (hidePassword && !HasPersistablePassword))) {
- ReplacePasswordPwd(out connectionString, false);
- }
- return ((null != connectionString) ? connectionString : "");
- }
- internal string UsersConnectionStringForTrace() {
- return UsersConnectionString(true, true);
- }
- internal bool HasBlankPassword {
- get {
- if (!ConvertValueToIntegratedSecurity()) {
- if (_parsetable.ContainsKey(KEY.Password)) {
- return ADP.IsEmpty((string)_parsetable[KEY.Password]);
- } else
- if (_parsetable.ContainsKey(SYNONYM.Pwd)) {
- return ADP.IsEmpty((string)_parsetable[SYNONYM.Pwd]); // MDAC 83097
- } else {
- return ((_parsetable.ContainsKey(KEY.User_ID) && !ADP.IsEmpty((string)_parsetable[KEY.User_ID])) || (_parsetable.ContainsKey(SYNONYM.UID) && !ADP.IsEmpty((string)_parsetable[SYNONYM.UID])));
- }
- }
- return false;
- }
- }
- internal bool HasPersistablePassword {
- get {
- if (HasPasswordKeyword) {
- return ConvertValueToBoolean(KEY.Persist_Security_Info, false);
- }
- return true; // no password means persistable password so we don't have to munge
- }
- }
- public bool IsEmpty {
- get { return (null == _keyChain); }
- }
- internal Hashtable Parsetable {
- get { return _parsetable; }
- }
- public ICollection Keys {
- get { return _parsetable.Keys; }
- }
- public string this[string keyword] {
- get { return (string)_parsetable[keyword]; }
- }
- internal static void AppendKeyValuePairBuilder(StringBuilder builder, string keyName, string keyValue, bool useOdbcRules) {
- ADP.CheckArgumentNull(builder, "builder");
- ADP.CheckArgumentLength(keyName, "keyName");
- if ((null == keyName) || !ConnectionStringValidKeyRegex.IsMatch(keyName)) {
- throw ADP.InvalidKeyname(keyName);
- }
- if ((null != keyValue) && !IsValueValidInternal(keyValue)) {
- throw ADP.InvalidValue(keyName);
- }
- if ((0 < builder.Length) && (';' != builder[builder.Length-1])) {
- builder.Append(";");
- }
- if (useOdbcRules) {
- builder.Append(keyName);
- }
- else {
- builder.Append(keyName.Replace("=", "=="));
- }
- builder.Append("=");
- if (null != keyValue) { // else <keyword>=;
- if (useOdbcRules) {
- if ((0 < keyValue.Length) &&
- (('{' == keyValue[0]) || (0 <= keyValue.IndexOf(';')) || (0 == String.Compare(DbConnectionStringKeywords.Driver, keyName, StringComparison.OrdinalIgnoreCase))) &&
- !ConnectionStringQuoteOdbcValueRegex.IsMatch(keyValue))
- {
- // always quote Driver value (required for ODBC Version 2.65 and earlier)
- // always quote values that contain a ';'
- builder.Append('{').Append(keyValue.Replace("}", "}}")).Append('}');
- }
- else {
- builder.Append(keyValue);
- }
- }
- else if (ConnectionStringQuoteValueRegex.IsMatch(keyValue)) {
- // <value> -> <value>
- builder.Append(keyValue);
- }
- else if ((-1 != keyValue.IndexOf('\"')) && (-1 == keyValue.IndexOf('\''))) {
- // <val"ue> -> <'val"ue'>
- builder.Append('\'');
- builder.Append(keyValue);
- builder.Append('\'');
- }
- else {
- // <val'ue> -> <"val'ue">
- // <=value> -> <"=value">
- // <;value> -> <";value">
- // < value> -> <" value">
- // <va lue> -> <"va lue">
- // <va'"lue> -> <"va'""lue">
- builder.Append('\"');
- builder.Append(keyValue.Replace("\"", "\"\""));
- builder.Append('\"');
- }
- }
- }
- public bool ConvertValueToBoolean(string keyName, bool defaultValue) {
- object value = _parsetable[keyName];
- if (null == value) {
- return defaultValue;
- }
- return ConvertValueToBooleanInternal(keyName, (string) value);
- }
- internal static bool ConvertValueToBooleanInternal(string keyName, string stringValue) {
- if (CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes"))
- return true;
- else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no"))
- return false;
- else {
- string tmp = stringValue.Trim(); // Remove leading & trailing white space.
- if (CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes"))
- return true;
- else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no"))
- return false;
- else {
- throw ADP.InvalidConnectionOptionValue(keyName);
- }
- }
- }
- // same as Boolean, but with SSPI thrown in as valid yes
- public bool ConvertValueToIntegratedSecurity() {
- object value = _parsetable[KEY.Integrated_Security];
- if (null == value) {
- return false;
- }
- return ConvertValueToIntegratedSecurityInternal((string) value);
- }
- internal bool ConvertValueToIntegratedSecurityInternal(string stringValue) {
- if (CompareInsensitiveInvariant(stringValue, "sspi") || CompareInsensitiveInvariant(stringValue, "true") || CompareInsensitiveInvariant(stringValue, "yes"))
- return true;
- else if (CompareInsensitiveInvariant(stringValue, "false") || CompareInsensitiveInvariant(stringValue, "no"))
- return false;
- else {
- string tmp = stringValue.Trim(); // Remove leading & trailing white space.
- if (CompareInsensitiveInvariant(tmp, "sspi") || CompareInsensitiveInvariant(tmp, "true") || CompareInsensitiveInvariant(tmp, "yes"))
- return true;
- else if (CompareInsensitiveInvariant(tmp, "false") || CompareInsensitiveInvariant(tmp, "no"))
- return false;
- else {
- throw ADP.InvalidConnectionOptionValue(KEY.Integrated_Security);
- }
- }
- }
- public int ConvertValueToInt32(string keyName, int defaultValue) {
- object value = _parsetable[keyName];
- if (null == value) {
- return defaultValue;
- }
- return ConvertToInt32Internal(keyName, (string) value);
- }
- internal static int ConvertToInt32Internal(string keyname, string stringValue) {
- try {
- return System.Int32.Parse(stringValue, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture);
- }
- catch (FormatException e) {
- throw ADP.InvalidConnectionOptionValue(keyname, e);
- }
- catch (OverflowException e) {
- throw ADP.InvalidConnectionOptionValue(keyname, e);
- }
- }
- public string ConvertValueToString(string keyName, string defaultValue) {
- string value = (string)_parsetable[keyName];
- return ((null != value) ? value : defaultValue);
- }
- static private bool CompareInsensitiveInvariant(string strvalue, string strconst) {
- return (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst));
- }
- public bool ContainsKey(string keyword) {
- return _parsetable.ContainsKey(keyword);
- }
- protected internal virtual System.Security.PermissionSet CreatePermissionSet() {
- return null;
- }
- internal void DemandPermission() {
- if (null == _permissionset) {
- _permissionset = CreatePermissionSet();
- }
- _permissionset.Demand();
- }
- protected internal virtual string Expand() {
- return _usersConnectionString;
- }
- // SxS notes:
- // * this method queries "DataDirectory" value from the current AppDomain.
- // This string is used for to replace "!DataDirectory!" values in the connection string, it is not considered as an "exposed resource".
- // * This method uses GetFullPath to validate that root path is valid, the result is not exposed out.
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
- internal static string ExpandDataDirectory(string keyword, string value, ref string datadir) {
- string fullPath = null;
- if ((null != value) && value.StartsWith(DataDirectory, StringComparison.OrdinalIgnoreCase)) {
- string rootFolderPath = datadir;
- if (null == rootFolderPath) {
- // find the replacement path
- object rootFolderObject = AppDomain.CurrentDomain.GetData("DataDirectory");
- rootFolderPath = (rootFolderObject as string);
- if ((null != rootFolderObject) && (null == rootFolderPath)) {
- throw ADP.InvalidDataDirectory();
- }
- else if (ADP.IsEmpty(rootFolderPath)) {
- rootFolderPath = AppDomain.CurrentDomain.BaseDirectory;
- }
- if (null == rootFolderPath) {
- rootFolderPath = "";
- }
- // cache the |DataDir| for ExpandDataDirectories
- datadir = rootFolderPath;
- }
- // We don't know if rootFolderpath ends with '\', and we don't know if the given name starts with onw
- int fileNamePosition = DataDirectory.Length; // filename starts right after the '|datadirectory|' keyword
- bool rootFolderEndsWith = (0 < rootFolderPath.Length) && rootFolderPath[rootFolderPath.Length-1] == '\\';
- bool fileNameStartsWith = (fileNamePosition < value.Length) && value[fileNamePosition] == '\\';
- // replace |datadirectory| with root folder path
- if (!rootFolderEndsWith && !fileNameStartsWith) {
- // need to insert '\'
- fullPath = rootFolderPath + '\\' + value.Substring(fileNamePosition);
- }
- else if (rootFolderEndsWith && fileNameStartsWith) {
- // need to strip one out
- fullPath = rootFolderPath + value.Substring(fileNamePosition+1);
- }
- else {
- // simply concatenate the strings
- fullPath = rootFolderPath + value.Substring(fileNamePosition);
- }
- // verify root folder path is a real path without unexpected "..\"
- if (!ADP.GetFullPath(fullPath).StartsWith(rootFolderPath, StringComparison.Ordinal)) {
- throw ADP.InvalidConnectionOptionValue(keyword);
- }
- }
- return fullPath;
- }
- internal string ExpandDataDirectories(ref string filename, ref int position) {
- string value = null;
- StringBuilder builder = new StringBuilder(_usersConnectionString.Length);
- string datadir = null;
- int copyPosition = 0;
- bool expanded = false;
- for(NameValuePair current = _keyChain; null != current; current = current.Next) {
- value = current.Value;
- // remove duplicate keyswords from connectionstring
- //if ((object)this[current.Name] != (object)value) {
- // expanded = true;
- // copyPosition += current.Length;
- // continue;
- //}
- // There is a set of keywords we explictly do NOT want to expand |DataDirectory| on
- if (UseOdbcRules) {
- switch(current.Name) {
- case DbConnectionOptionKeywords.Driver:
- case DbConnectionOptionKeywords.Pwd:
- case DbConnectionOptionKeywords.UID:
- break;
- default:
- value = ExpandDataDirectory(current.Name, value, ref datadir);
- break;
- }
- }
- else {
- switch(current.Name) {
- case DbConnectionOptionKeywords.Provider:
- case DbConnectionOptionKeywords.DataProvider:
- case DbConnectionOptionKeywords.RemoteProvider:
- case DbConnectionOptionKeywords.ExtendedProperties:
- case DbConnectionOptionKeywords.UserID:
- case DbConnectionOptionKeywords.Password:
- case DbConnectionOptionKeywords.UID:
- case DbConnectionOptionKeywords.Pwd:
- break;
- default:
- value = ExpandDataDirectory(current.Name, value, ref datadir);
- break;
- }
- }
- if (null == value) {
- value = current.Value;
- }
- if (UseOdbcRules || (DbConnectionOptionKeywords.FileName != current.Name)) {
- if (value != current.Value) {
- expanded = true;
- AppendKeyValuePairBuilder(builder, current.Name, value, UseOdbcRules);
- builder.Append(';');
- }
- else {
- builder.Append(_usersConnectionString, copyPosition, current.Length);
- }
- }
- else {
- // strip out 'File Name=myconnection.udl' for OleDb
- // remembering is value for which UDL file to open
- // and where to insert the strnig
- expanded = true;
- filename = value;
- position = builder.Length;
- }
- copyPosition += current.Length;
- }
- if (expanded) {
- value = builder.ToString();
- }
- else {
- value = null;
- }
- return value;
- }
- internal string ExpandKeyword(string keyword, string replacementValue) {
- // preserve duplicates, updated keyword value with replacement value
- // if keyword not specified, append to end of the string
- bool expanded = false;
- int copyPosition = 0;
- StringBuilder builder = new StringBuilder(_usersConnectionString.Length);
- for(NameValuePair current = _keyChain; null != current; current = current.Next) {
- if ((current.Name == keyword) && (current.Value == this[keyword])) {
- // only replace the parse end-result value instead of all values
- // so that when duplicate-keywords occur other original values remain in place
- AppendKeyValuePairBuilder(builder, current.Name, replacementValue, UseOdbcRules);
- builder.Append(';');
- expanded = true;
- }
- else {
- builder.Append(_usersConnectionString, copyPosition, current.Length);
- }
- copyPosition += current.Length;
- }
- if (!expanded) {
- //
- Debug.Assert(!UseOdbcRules, "ExpandKeyword not ready for Odbc");
- AppendKeyValuePairBuilder(builder, keyword, replacementValue, UseOdbcRules);
- }
- return builder.ToString();
- }
- #if DEBUG
- [System.Diagnostics.Conditional("DEBUG")]
- private static void DebugTraceKeyValuePair(string keyname, string keyvalue, Hashtable synonyms) {
- if (Bid.AdvancedOn) {
- Debug.Assert(keyname == keyname.ToLower(CultureInfo.InvariantCulture), "missing ToLower");
- string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname);
- if ((KEY.Password != realkeyname) && (SYNONYM.Pwd != realkeyname)) { // don't trace passwords ever!
- if (null != keyvalue) {
- Bid.Trace("<comm.DbConnectionOptions|INFO|ADV> KeyName='%ls', KeyValue='%ls'\n", keyname, keyvalue);
- }
- else {
- Bid.Trace("<comm.DbConnectionOptions|INFO|ADV> KeyName='%ls'\n", keyname);
- }
- }
- }
- }
- #endif
- static private string GetKeyName(StringBuilder buffer) {
- int count = buffer.Length;
- while ((0 < count) && Char.IsWhiteSpace(buffer[count-1])) {
- count--; // trailing whitespace
- }
- return buffer.ToString(0, count).ToLower(CultureInfo.InvariantCulture);
- }
- static private string GetKeyValue(StringBuilder buffer, bool trimWhitespace) {
- int count = buffer.Length;
- int index = 0;
- if (trimWhitespace) {
- while ((index < count) && Char.IsWhiteSpace(buffer[index])) {
- index++; // leading whitespace
- }
- while ((0 < count) && Char.IsWhiteSpace(buffer[count-1])) {
- count--; // trailing whitespace
- }
- }
- return buffer.ToString(index, count - index);
- }
- // transistion states used for parsing
- private enum ParserState {
- NothingYet=1, //start point
- Key,
- KeyEqual,
- KeyEnd,
- UnquotedValue,
- DoubleQuoteValue,
- DoubleQuoteValueQuote,
- SingleQuoteValue,
- SingleQuoteValueQuote,
- BraceQuoteValue,
- BraceQuoteValueQuote,
- QuotedValueEnd,
- NullTermination,
- };
- static internal int GetKeyValuePair(string connectionString, int currentPosition, StringBuilder buffer, bool useOdbcRules, out string keyname, out string keyvalue) {
- int startposition = currentPosition;
- buffer.Length = 0;
- keyname = null;
- keyvalue = null;
- char currentChar = '\0';
- ParserState parserState = ParserState.NothingYet;
- int length = connectionString.Length;
- for (; currentPosition < length; ++currentPosition) {
- currentChar = connectionString[currentPosition];
- switch(parserState) {
- case ParserState.NothingYet: // [\\s;]*
- if ((';' == currentChar) || Char.IsWhiteSpace(currentChar)) {
- continue;
- }
- if ('\0' == currentChar) { parserState = ParserState.NullTermination; continue; } // MDAC 83540
- if (Char.IsControl(currentChar)) { throw ADP.ConnectionStringSyntax(startposition); }
- startposition = currentPosition;
- if ('=' != currentChar) { // MDAC 86902
- parserState = ParserState.Key;
- break;
- }
- else {
- parserState = ParserState.KeyEqual;
- continue;
- }
- case ParserState.Key: // (?<key>([^=\\s\\p{Cc}]|\\s+[^=\\s\\p{Cc}]|\\s+==|==)+)
- if ('=' == currentChar) { parserState = ParserState.KeyEqual; continue; }
- if (Char.IsWhiteSpace(currentChar)) { break; }
- if (Char.IsControl(currentChar)) { throw ADP.ConnectionStringSyntax(startposition); }
- break;
- case ParserState.KeyEqual: // \\s*=(?!=)\\s*
- if (!useOdbcRules && '=' == currentChar) { parserState = ParserState.Key; break; }
- keyname = GetKeyName(buffer);
- if (ADP.IsEmpty(keyname)) { throw ADP.ConnectionStringSyntax(startposition); }
- buffer.Length = 0;
- parserState = ParserState.KeyEnd;
- goto case ParserState.KeyEnd;
- case ParserState.KeyEnd:
- if (Char.IsWhiteSpace(currentChar)) { continue; }
- if (useOdbcRules) {
- if ('{' == currentChar) { parserState = ParserState.BraceQuoteValue; break; }
- }
- else {
- if ('\'' == currentChar) { parserState = ParserState.SingleQuoteValue; continue; }
- if ('"' == currentChar) { parserState = ParserState.DoubleQuoteValue; continue; }
- }
- if (';' == currentChar) { goto ParserExit; }
- if ('\0' == currentChar) { goto ParserExit; }
- if (Char.IsControl(currentChar)) { throw ADP.ConnectionStringSyntax(startposition); }
- parserState = ParserState.UnquotedValue;
- break;
- case ParserState.UnquotedValue: // "((?![\"'\\s])" + "([^;\\s\\p{Cc}]|\\s+[^;\\s\\p{Cc}])*" + "(?<![\"']))"
- if (Char.IsWhiteSpace(currentChar)) { break; }
- if (Char.IsControl(currentChar) || ';' == currentChar) { goto ParserExit; }
- break;
- case ParserState.DoubleQuoteValue: // "(\"([^\"\u0000]|\"\")*\")"
- if ('"' == currentChar) { parserState = ParserState.DoubleQuoteValueQuote; continue; }
- if ('\0' == currentChar) { throw ADP.ConnectionStringSyntax(startposition); }
- break;
- case ParserState.DoubleQuoteValueQuote:
- if ('"' == currentChar) { parserState = ParserState.DoubleQuoteValue; break; }
- keyvalue = GetKeyValue(buffer, false);
- parserState = ParserState.QuotedValueEnd;
- goto case ParserState.QuotedValueEnd;
- case ParserState.SingleQuoteValue: // "('([^'\u0000]|'')*')"
- if ('\'' == currentChar) { parserState = ParserState.SingleQuoteValueQuote; continue; }
- if ('\0' == currentChar) { throw ADP.ConnectionStringSyntax(startposition); }
- break;
- case ParserState.SingleQuoteValueQuote:
- if ('\'' == currentChar) { parserState = ParserState.SingleQuoteValue; break; }
- keyvalue = GetKeyValue(buffer, false);
- parserState = ParserState.QuotedValueEnd;
- goto case ParserState.QuotedValueEnd;
- case ParserState.BraceQuoteValue: // "(\\{([^\\}\u0000]|\\}\\})*\\})"
- if ('}' == currentChar) { parserState = ParserState.BraceQuoteValueQuote; break; }
- if ('\0' == currentChar) { throw ADP.ConnectionStringSyntax(startposition); }
- break;
- case ParserState.BraceQuoteValueQuote:
- if ('}' == currentChar) { parserState = ParserState.BraceQuoteValue; break; }
- keyvalue = GetKeyValue(buffer, false);
- parserState = ParserState.QuotedValueEnd;
- goto case ParserState.QuotedValueEnd;
- case ParserState.QuotedValueEnd:
- if (Char.IsWhiteSpace(currentChar)) { continue; }
- if (';' == currentChar) { goto ParserExit; }
- if ('\0' == currentChar) { parserState = ParserState.NullTermination; continue; } // MDAC 83540
- throw ADP.ConnectionStringSyntax(startposition); // unbalanced single quote
- case ParserState.NullTermination: // [\\s;\u0000]*
- if ('\0' == currentChar) { continue; }
- if (Char.IsWhiteSpace(currentChar)) { continue; } // MDAC 83540
- throw ADP.ConnectionStringSyntax(currentPosition);
- default:
- throw ADP.InternalError(ADP.InternalErrorCode.InvalidParserState1);
- }
- buffer.Append(currentChar);
- }
- ParserExit:
- switch (parserState) {
- case ParserState.Key:
- case ParserState.DoubleQuoteValue:
- case ParserState.SingleQuoteValue:
- case ParserState.BraceQuoteValue:
- // keyword not found/unbalanced double/single quote
- throw ADP.ConnectionStringSyntax(startposition);
- case ParserState.KeyEqual:
- // equal sign at end of line
- keyname = GetKeyName(buffer);
- if (ADP.IsEmpty(keyname)) { throw ADP.ConnectionStringSyntax(startposition); }
- break;
- case ParserState.UnquotedValue:
- // unquoted value at end of line
- keyvalue = GetKeyValue(buffer, true);
- char tmpChar = keyvalue[keyvalue.Length - 1];
- if (!useOdbcRules && (('\'' == tmpChar) || ('"' == tmpChar))) {
- throw ADP.ConnectionStringSyntax(startposition); // unquoted value must not end in quote, except for odbc
- }
- break;
- case ParserState.DoubleQuoteValueQuote:
- case ParserState.SingleQuoteValueQuote:
- case ParserState.BraceQuoteValueQuote:
- case ParserState.QuotedValueEnd:
- // quoted value at end of line
- keyvalue = GetKeyValue(buffer, false);
- break;
- case ParserState.NothingYet:
- case ParserState.KeyEnd:
- case ParserState.NullTermination:
- // do nothing
- break;
- default:
- throw ADP.InternalError(ADP.InternalErrorCode.InvalidParserState2);
- }
- if ((';' == currentChar) && (currentPosition < connectionString.Length)) {
- currentPosition++;
- }
- return currentPosition;
- }
- static private bool IsValueValidInternal(string keyvalue) {
- if (null != keyvalue)
- {
- #if DEBUG
- bool compValue = ConnectionStringValidValueRegex.IsMatch(keyvalue);
- Debug.Assert((-1 == keyvalue.IndexOf('\u0000')) == compValue, "IsValueValid mismatch with regex");
- #endif
- return (-1 == keyvalue.IndexOf('\u0000'));
- }
- return true;
- }
- static private bool IsKeyNameValid(string keyname) {
- if (null != keyname) {
- #if DEBUG
- bool compValue = ConnectionStringValidKeyRegex.IsMatch(keyname);
- Debug.Assert(((0 < keyname.Length) && (';' != keyname[0]) && !Char.IsWhiteSpace(keyname[0]) && (-1 == keyname.IndexOf('\u0000'))) == compValue, "IsValueValid mismatch with regex");
- #endif
- return ((0 < keyname.Length) && (';' != keyname[0]) && !Char.IsWhiteSpace(keyname[0]) && (-1 == keyname.IndexOf('\u0000')));
- }
- return false;
- }
- #if DEBUG
- private static Hashtable SplitConnectionString(string connectionString, Hashtable synonyms, bool firstKey) {
- Hashtable parsetable = new Hashtable();
- Regex parser = (firstKey ? ConnectionStringRegexOdbc : ConnectionStringRegex);
- const int KeyIndex = 1, ValueIndex = 2;
- Debug.Assert(KeyIndex == parser.GroupNumberFromName("key"), "wrong key index");
- Debug.Assert(ValueIndex == parser.GroupNumberFromName("value"), "wrong value index");
- if (null != connectionString) {
- Match match = parser.Match(connectionString);
- if (!match.Success || (match.Length != connectionString.Length)) {
- throw ADP.ConnectionStringSyntax(match.Length);
- }
- int indexValue = 0;
- CaptureCollection keyvalues = match.Groups[ValueIndex].Captures;
- foreach(Capture keypair in match.Groups[KeyIndex].Captures) {
- string keyname = (firstKey ? keypair.Value : keypair.Value.Replace("==", "=")).ToLower(CultureInfo.InvariantCulture);
- string keyvalue = keyvalues[indexValue++].Value;
- if (0 < keyvalue.Length) {
- if (!firstKey) {
- switch(keyvalue[0]) {
- case '\"':
- keyvalue = keyvalue.Substring(1, keyvalue.Length-2).Replace("\"\"", "\"");
- break;
- case '\'':
- keyvalue = keyvalue.Substring(1, keyvalue.Length-2).Replace("\'\'", "\'");
- break;
- default:
- break;
- }
- }
- }
- else {
- keyvalue = null;
- }
- DebugTraceKeyValuePair(keyname, keyvalue, synonyms);
- string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname);
- if (!IsKeyNameValid(realkeyname)) {
- throw ADP.KeywordNotSupported(keyname);
- }
- if (!firstKey || !parsetable.ContainsKey(realkeyname)) {
- parsetable[realkeyname] = keyvalue; // last key-value pair wins (or first)
- }
- }
- }
- return parsetable;
- }
- private static void ParseComparison(Hashtable parsetable, string connectionString, Hashtable synonyms, bool firstKey, Exception e) {
- try {
- Hashtable parsedvalues = SplitConnectionString(connectionString, synonyms, firstKey);
- foreach(DictionaryEntry entry in parsedvalues) {
- string keyname = (string) entry.Key;
- string value1 = (string) entry.Value;
- string value2 = (string) parsetable[keyname];
- Debug.Assert(parsetable.Contains(keyname), "ParseInternal code vs. regex mismatch keyname <" + keyname + ">");
- Debug.Assert(value1 == value2, "ParseInternal code vs. regex mismatch keyvalue <" + value1 + "> <" + value2 +">");
- }
- }
- catch(ArgumentException f) {
- if (null != e) {
- string msg1 = e.Message;
- string msg2 = f.Message;
- const string KeywordNotSupportedMessagePrefix = "Keyword not supported:";
- const string WrongFormatMessagePrefix = "Format of the initialization string";
- bool isEquivalent = (msg1 == msg2);
- if (!isEquivalent)
- {
- // VSTFDEVDIV 479587: we also accept cases were Regex parser (debug only) reports "wrong format" and
- // retail parsing code reports format exception in different location or "keyword not supported"
- if (msg2.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal)) {
- if (msg1.StartsWith(KeywordNotSupportedMessagePrefix, StringComparison.Ordinal) || msg1.StartsWith(WrongFormatMessagePrefix, StringComparison.Ordinal)) {
- isEquivalent = true;
- }
- }
- }
- Debug.Assert(isEquivalent, "ParseInternal code vs regex message mismatch: <"+msg1+"> <"+msg2+">");
- }
- else {
- Debug.Assert(false, "ParseInternal code vs regex throw mismatch " + f.Message);
- }
- e = null;
- }
- if (null != e) {
- Debug.Assert(false, "ParseInternal code threw exception vs regex mismatch");
- }
- }
- #endif
- private static NameValuePair ParseInternal(Hashtable parsetable, string connectionString, bool buildChain, Hashtable synonyms, bool firstKey) {
- Debug.Assert(null != connectionString, "null connectionstring");
- StringBuilder buffer = new StringBuilder();
- NameValuePair localKeychain = null, keychain = null;
- #if DEBUG
- try {
- #endif
- int nextStartPosition = 0;
- int endPosition = connectionString.Length;
- while (nextStartPosition < endPosition) {
- int startPosition = nextStartPosition;
- string keyname, keyvalue;
- nextStartPosition = GetKeyValuePair(connectionString, startPosition, buffer, firstKey, out keyname, out keyvalue);
- if (ADP.IsEmpty(keyname)) {
- // if (nextStartPosition != endPosition) { throw; }
- break;
- }
- #if DEBUG
- DebugTraceKeyValuePair(keyname, keyvalue, synonyms);
- Debug.Assert(IsKeyNameValid(keyname), "ParseFailure, invalid keyname");
- Debug.Assert(IsValueValidInternal(keyvalue), "parse failure, invalid keyvalue");
- #endif
- string realkeyname = ((null != synonyms) ? (string)synonyms[keyname] : keyname);
- if (!IsKeyNameValid(realkeyname)) {
- throw ADP.KeywordNotSupported(keyname);
- }
- if (!firstKey || !parsetable.Contains(realkeyname)) {
- parsetable[realkeyname] = keyvalue; // last key-value pair wins (or first)
- }
- if(null != localKeychain) {
- localKeychain = localKeychain.Next = new NameValuePair(realkeyname, keyvalue, nextStartPosition - startPosition);
- }
- else if (buildChain) { // first time only - don't contain modified chain from UDL file
- keychain = localKeychain = new NameValuePair(realkeyname, keyvalue, nextStartPosition - startPosition);
- }
- }
- #if DEBUG
- }
- catch(ArgumentException e) {
- ParseComparison(parsetable, connectionString, synonyms, firstKey, e);
- throw;
- }
- ParseComparison(parsetable, connectionString, synonyms, firstKey, null);
- #endif
- return keychain;
- }
- internal NameValuePair ReplacePasswordPwd(out string constr, bool fakePassword) {
- bool expanded = false;
- int copyPosition = 0;
- NameValuePair head = null, tail = null, next = null;
- StringBuilder builder = new StringBuilder(_usersConnectionString.Length);
- for(NameValuePair current = _keyChain; null != current; current = current.Next) {
- if ((KEY.Password != current.Name) && (SYNONYM.Pwd != current.Name)) {
- builder.Append(_usersConnectionString, copyPosition, current.Length);
- if (fakePassword) {
- next = new NameValuePair(current.Name, current.Value, current.Length);
- }
- }
- else if (fakePassword) { // replace user password/pwd value with *
- const string equalstar = "=*;";
- builder.Append(current.Name).Append(equalstar);
- next = new NameValuePair(current.Name, "*", current.Name.Length + equalstar.Length);
- expanded = true;
- }
- else { // drop the password/pwd completely in returning for user
- expanded = true;
- }
- if (fakePassword) {
- if (null != tail) {
- tail = tail.Next = next;
- }
- else {
- tail = head = next;
- }
- }
- copyPosition += current.Length;
- }
- Debug.Assert(expanded, "password/pwd was not removed");
- constr = builder.ToString();
- return head;
- }
- internal static void ValidateKeyValuePair(string keyword, string value) {
- if ((null == keyword) || !ConnectionStringValidKeyRegex.IsMatch(keyword)) {
- throw ADP.InvalidKeyname(keyword);
- }
- if ((null != value) && !ConnectionStringValidValueRegex.IsMatch(value)) {
- throw ADP.InvalidValue(keyword);
- }
- }
- }
- }
|