//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] // [....] //------------------------------------------------------------------------------ namespace System.Data.OleDb { using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Globalization; using System.IO; using System.Security; using System.Security.Permissions; using System.Text; using Microsoft.Win32; using System.Runtime.Versioning; internal struct SchemaSupport { internal Guid _schemaRowset; internal int _restrictions; } internal sealed class OleDbConnectionString : 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 internal static class KEY { internal const string Asynchronous_Processing = "asynchronous processing"; internal const string Connect_Timeout = "connect timeout"; internal const string Data_Provider = "data provider"; internal const string Data_Source = "data source"; internal const string Extended_Properties = "extended properties"; internal const string File_Name = "file name"; internal const string Initial_Catalog = "initial catalog"; internal const string Ole_DB_Services = "ole db services"; internal const string Persist_Security_Info = "persist security info"; internal const string Prompt = "prompt"; internal const string Provider = "provider"; internal const string RemoteProvider = "remote provider"; internal const string WindowHandle = "window handle"; } // registry key and dword value entry for udl pooling private static class UDL { internal const string Header = "\xfeff[oledb]\r\n; Everything after this line is an OLE DB initstring\r\n"; internal const string Location = "SOFTWARE\\Microsoft\\DataAccess\\Udl Pooling"; internal const string Pooling = "Cache Size"; static internal volatile bool _PoolSizeInit; static internal int _PoolSize; static internal volatile Dictionary _Pool; static internal object _PoolLock = new object(); } private static class VALUES { internal const string NoPrompt = "noprompt"; } // set during ctor internal readonly bool PossiblePrompt; internal readonly string ActualConnectionString; // cached value passed to GetDataSource private readonly string _expandedConnectionString; internal SchemaSupport[] _schemaSupport; internal int _sqlSupport; internal bool _supportMultipleResults; internal bool _supportIRow; internal bool _hasSqlSupport; internal bool _hasSupportMultipleResults, _hasSupportIRow; private int _oledbServices; // these are cached delegates (per unique connectionstring) internal UnsafeNativeMethods.IUnknownQueryInterface DangerousDataSourceIUnknownQueryInterface; internal UnsafeNativeMethods.IDBInitializeInitialize DangerousIDBInitializeInitialize; internal UnsafeNativeMethods.IDBCreateSessionCreateSession DangerousIDBCreateSessionCreateSession; internal UnsafeNativeMethods.IDBCreateCommandCreateCommand DangerousIDBCreateCommandCreateCommand; // since IDBCreateCommand interface may not be supported for a particular provider (only IOpenRowset) // we cache that fact rather than call QueryInterface on every call to Open internal bool HaveQueriedForCreateCommand; // SxS: if user specifies a value for "File Name=" (UDL) in connection string, OleDbConnectionString will load the connection string // from the UDL file. The UDL file is opened as FileMode.Open, FileAccess.Read, FileShare.Read, allowing concurrent access to it. [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] internal OleDbConnectionString(string connectionString, bool validate) : base(connectionString) { string prompt = this[KEY.Prompt]; PossiblePrompt = ((!ADP.IsEmpty(prompt) && (0 != String.Compare(prompt, VALUES.NoPrompt, StringComparison.OrdinalIgnoreCase))) || !ADP.IsEmpty(this[KEY.WindowHandle])); if (!IsEmpty) { string udlConnectionString = null; if (!validate) { int position = 0; string udlFileName = null; _expandedConnectionString = ExpandDataDirectories(ref udlFileName, ref position); if (!ADP.IsEmpty(udlFileName)) { // fail via new FileStream vs. GetFullPath udlFileName = ADP.GetFullPath(udlFileName); // MDAC 82833 } if (null != udlFileName) { udlConnectionString = LoadStringFromStorage(udlFileName); if (!ADP.IsEmpty(udlConnectionString)) { _expandedConnectionString = _expandedConnectionString.Substring(0, position) + udlConnectionString + ';' + _expandedConnectionString.Substring(position); } } } if (validate || ADP.IsEmpty(udlConnectionString)) { ActualConnectionString = ValidateConnectionString(connectionString); } } } internal int ConnectTimeout { get { return base.ConvertValueToInt32(KEY.Connect_Timeout, ADP.DefaultConnectionTimeout); } } internal string DataSource { get { return base.ConvertValueToString(KEY.Data_Source, ADP.StrEmpty); } } internal string InitialCatalog { get { return base.ConvertValueToString(KEY.Initial_Catalog, ADP.StrEmpty); } } internal string Provider { get { Debug.Assert(!ADP.IsEmpty(this[KEY.Provider]), "no Provider"); return this[KEY.Provider]; } } internal int OleDbServices { get { return _oledbServices; } } internal SchemaSupport[] SchemaSupport { // OleDbConnection.GetSchemaRowsetInformation get { return _schemaSupport; } set { _schemaSupport = value; } } protected internal override System.Security.PermissionSet CreatePermissionSet() { System.Security.PermissionSet permissionSet; if (PossiblePrompt) { permissionSet = new NamedPermissionSet("FullTrust"); } else { permissionSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.None); permissionSet.AddPermission(new OleDbPermission(this)); } return permissionSet; } protected internal override string Expand() { if (null != _expandedConnectionString) { return _expandedConnectionString; } else { return base.Expand(); } } internal int GetSqlSupport(OleDbConnection connection) { int sqlSupport = _sqlSupport; if (!_hasSqlSupport) { object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_SQLSUPPORT); if (value is Int32) { // not OleDbPropertyStatus sqlSupport = (int) value; } _sqlSupport = sqlSupport; _hasSqlSupport = true; } return sqlSupport; } internal bool GetSupportIRow(OleDbConnection connection, OleDbCommand command) { bool supportIRow = _supportIRow; if (!_hasSupportIRow) { object value = command.GetPropertyValue(OleDbPropertySetGuid.Rowset, ODB.DBPROP_IRow); // SQLOLEDB always returns VARIANT_FALSE for DBPROP_IROW, so base the answer on existance supportIRow = !(value is OleDbPropertyStatus); _supportIRow = supportIRow; _hasSupportIRow = true; } return supportIRow; } internal bool GetSupportMultipleResults(OleDbConnection connection) { bool supportMultipleResults = _supportMultipleResults; if (!_hasSupportMultipleResults) { object value = connection.GetDataSourcePropertyValue(OleDbPropertySetGuid.DataSourceInfo, ODB.DBPROP_MULTIPLERESULTS); if (value is Int32) {// not OleDbPropertyStatus supportMultipleResults = (ODB.DBPROPVAL_MR_NOTSUPPORTED != (int) value); } _supportMultipleResults = supportMultipleResults; _hasSupportMultipleResults = true; } return supportMultipleResults; } static private int UdlPoolSize { // MDAC 69925 // SxS: UdpPoolSize reads registry value to get the pool size [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] get { int poolsize = UDL._PoolSize; if (!UDL._PoolSizeInit) { object value = ADP.LocalMachineRegistryValue(UDL.Location, UDL.Pooling); if (value is Int32) { poolsize = (int) value; poolsize = ((0 < poolsize) ? poolsize : 0); UDL._PoolSize = poolsize; } UDL._PoolSizeInit = true; } return poolsize; } } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] static private string LoadStringFromStorage(string udlfilename) { string udlConnectionString = null; Dictionary udlcache = UDL._Pool; if ((null == udlcache) || !udlcache.TryGetValue(udlfilename, out udlConnectionString)) { udlConnectionString = LoadStringFromFileStorage(udlfilename); if (null != udlConnectionString) { Debug.Assert(!ADP.IsEmpty(udlfilename), "empty filename didn't fail"); if (0 < UdlPoolSize) { Debug.Assert(udlfilename == ADP.GetFullPath(udlfilename), "only cache full path filenames"); // MDAC 82833 if (null == udlcache) { udlcache = new Dictionary(); udlcache[udlfilename] = udlConnectionString; lock(UDL._PoolLock) { if (null != UDL._Pool) { udlcache = UDL._Pool; } else { UDL._Pool = udlcache; udlcache = null; } } } if (null != udlcache) { lock(udlcache) { udlcache[udlfilename] = udlConnectionString; } } } } } return udlConnectionString; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] static private string LoadStringFromFileStorage(string udlfilename) { // Microsoft Data Link File Format // The first two lines of a .udl file must have exactly the following contents in order to work properly: // [oledb] // ; Everything after this line is an OLE DB initstring // string connectionString = null; Exception failure = null; try { int hdrlength = ADP.CharSize*UDL.Header.Length; using(FileStream fstream = new FileStream(udlfilename, FileMode.Open, FileAccess.Read, FileShare.Read)) { long length = fstream.Length; if (length < hdrlength || (0 != length%ADP.CharSize)) { failure = ADP.InvalidUDL(); } else { byte[] bytes = new Byte[hdrlength]; int count = fstream.Read(bytes, 0, bytes.Length); if (count < hdrlength) { failure = ADP.InvalidUDL(); } else if (System.Text.Encoding.Unicode.GetString(bytes, 0, hdrlength) != UDL.Header) { failure = ADP.InvalidUDL(); } else { // please verify header before allocating memory block for connection string bytes = new Byte[length - hdrlength]; count = fstream.Read(bytes, 0, bytes.Length); connectionString = System.Text.Encoding.Unicode.GetString(bytes, 0, count); } } } } catch(Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } throw ADP.UdlFileError(e); } if (null != failure) { throw failure; } return connectionString.Trim(); } [ResourceExposure(ResourceScope.None)] // reads OleDbServices value for the provider [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private string ValidateConnectionString(string connectionString) { if (ConvertValueToBoolean(KEY.Asynchronous_Processing, false)) { throw ODB.AsynchronousNotSupported(); } int connectTimeout = ConvertValueToInt32(KEY.Connect_Timeout, 0); if (connectTimeout < 0) { throw ADP.InvalidConnectTimeoutValue(); } string progid = ConvertValueToString(KEY.Data_Provider, null); // MDAC 71923 if (null != progid) { progid = progid.Trim(); if (0 < progid.Length) { // don't fail on empty 'Data Provider' value ValidateProvider(progid); } } progid = ConvertValueToString(KEY.RemoteProvider, null); // MDAC 71923 if (null != progid) { progid = progid.Trim(); if (0 < progid.Length) { // don't fail on empty 'Data Provider' value ValidateProvider(progid); } } progid = ConvertValueToString(KEY.Provider, ADP.StrEmpty).Trim(); ValidateProvider(progid); // will fail on empty 'Provider' value // SQLBU VSTS 59322: initialize to default // If the value is not provided in connection string and OleDbServices registry key has not been set by the provider, // the default for the provider is -1 (all services are ON). // our default is -13, we turn off ODB.DBPROPVAL_OS_AGR_AFTERSESSION and ODB.DBPROPVAL_OS_CLIENTCURSOR flags _oledbServices = DbConnectionStringDefaults.OleDbServices; bool hasOleDBServices = (base.ContainsKey(KEY.Ole_DB_Services) && !ADP.IsEmpty((string)base[KEY.Ole_DB_Services])); if (!hasOleDBServices) { // don't touch registry if they have OLE DB Services string classid = (string) ADP.ClassesRootRegistryValue(progid + "\\CLSID", String.Empty); if ((null != classid) && (0 < classid.Length)) { // CLSID detection of 'Microsoft OLE DB Provider for ODBC Drivers' Guid classidProvider = new Guid(classid); if (ODB.CLSID_MSDASQL == classidProvider) { throw ODB.MSDASQLNotSupported(); } object tmp = ADP.ClassesRootRegistryValue("CLSID\\{" + classidProvider.ToString("D", CultureInfo.InvariantCulture) + "}", ODB.OLEDB_SERVICES); if (null != tmp) { // @devnote: some providers like MSDataShape don't have the OLEDB_SERVICES value // the MSDataShape provider doesn't support the 'Ole Db Services' keyword // hence, if the value doesn't exist - don't prepend to string try { _oledbServices = (int)tmp; } catch(InvalidCastException e) { ADP.TraceExceptionWithoutRethrow(e); } _oledbServices &= ~(ODB.DBPROPVAL_OS_AGR_AFTERSESSION | ODB.DBPROPVAL_OS_CLIENTCURSOR); // NT 347436, MDAC 58606 StringBuilder builder = new StringBuilder(); builder.Append(KEY.Ole_DB_Services); builder.Append("="); builder.Append(_oledbServices.ToString(CultureInfo.InvariantCulture)); builder.Append(";"); builder.Append(connectionString); connectionString = builder.ToString(); } } } else { // SQLBU VSTS 59322: parse the Ole Db Services value from connection string _oledbServices = ConvertValueToInt32(KEY.Ole_DB_Services, DbConnectionStringDefaults.OleDbServices); } return connectionString; } internal static bool IsMSDASQL(string progid) { return (("msdasql" == progid) || progid.StartsWith("msdasql.", StringComparison.Ordinal) || ("microsoft ole db provider for odbc drivers" == progid)); } static private void ValidateProvider(string progid) { if (ADP.IsEmpty(progid)) { throw ODB.NoProviderSpecified(); } if (ODB.MaxProgIdLength <= progid.Length) { // MDAC 63151 throw ODB.InvalidProviderSpecified(); } progid = progid.ToLower(CultureInfo.InvariantCulture); if (IsMSDASQL(progid)) { // fail msdasql even if not on the machine. throw ODB.MSDASQLNotSupported(); } } static internal void ReleaseObjectPool() { UDL._PoolSizeInit = false; UDL._Pool = null; } } }