#region License
// The PostgreSQL License
//
// Copyright (C) 2015 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
#if NET45 || NET451 || DNX451
using System.DirectoryServices;
using System.Security.Principal;
#endif
namespace Npgsql
{
///
/// Provides a simple way to create and manage the contents of connection strings used by
/// the class.
///
public sealed class NpgsqlConnectionStringBuilder : DbConnectionStringBuilder
{
#region Fields
///
/// Makes all valid keywords for a property to that property (e.g. User Name -> Username, UserId -> Username...)
///
static readonly Dictionary PropertiesByKeyword;
///
/// Maps CLR property names (e.g. BufferSize) to their canonical keyword name, which is the
/// property's [DisplayName] (e.g. Buffer Size)
///
static readonly Dictionary PropertyNameToCanonicalKeyword;
///
/// Maps each property to its [DefaultValue]
///
static readonly Dictionary PropertyDefaults;
static readonly string[] Empty = new string[0];
#endregion
#region Constructors
///
/// Initializes a new instance of the NpgsqlConnectionStringBuilder class.
///
public NpgsqlConnectionStringBuilder() { Init(); }
#if NET45 || NET451 || DNX451
///
/// Initializes a new instance of the NpgsqlConnectionStringBuilder class, optionally using ODBC rules for quoting values.
///
/// true to use {} to delimit fields; false to use quotation marks.
public NpgsqlConnectionStringBuilder(bool useOdbcRules) : base(useOdbcRules) { Init(); }
#endif
///
/// Initializes a new instance of the NpgsqlConnectionStringBuilder class and sets its .
///
public NpgsqlConnectionStringBuilder(string connectionString)
{
Init();
ConnectionString = connectionString;
}
void Init()
{
foreach (var kv in PropertyDefaults) {
kv.Key.SetValue(this, kv.Value);
base.Clear();
}
}
#endregion
#region Static initialization
static NpgsqlConnectionStringBuilder()
{
var properties = typeof(NpgsqlConnectionStringBuilder)
.GetProperties()
.Where(p => p.GetCustomAttribute() != null)
.ToArray();
Contract.Assume(properties.All(p => p.CanRead && p.CanWrite));
Contract.Assume(properties.All(p => p.GetCustomAttribute() != null));
PropertiesByKeyword = (
from p in properties
let displayName = p.GetCustomAttribute().DisplayName.ToUpperInvariant()
let propertyName = p.Name.ToUpperInvariant()
from k in new[] { displayName }
.Concat(propertyName != displayName ? new[] { propertyName } : Empty )
.Concat(p.GetCustomAttribute().Aliases
.Select(a => a.ToUpperInvariant())
)
.Select(k => new { Property = p, Keyword = k })
select k
).ToDictionary(t => t.Keyword, t => t.Property);
PropertyNameToCanonicalKeyword = properties.ToDictionary(
p => p.Name,
p => p.GetCustomAttribute().DisplayName
);
PropertyDefaults = properties
.Where(p => p.GetCustomAttribute() == null)
.ToDictionary(
p => p,
p => p.GetCustomAttribute() != null
? p.GetCustomAttribute().Value
: (p.PropertyType.GetTypeInfo().IsValueType ? Activator.CreateInstance(p.PropertyType) : null)
);
}
#endregion
#region Non-static property handling
///
/// Gets or sets the value associated with the specified key.
///
/// The key of the item to get or set.
/// The value associated with the specified key.
public override object this[string keyword]
{
get
{
object value;
if (!TryGetValue(keyword, out value)) {
throw new ArgumentException("Keyword not supported: " + keyword, nameof(keyword));
}
return value;
}
set
{
if (value == null) {
Remove(keyword);
return;
}
var p = GetProperty(keyword);
try {
object convertedValue;
if (p.PropertyType.GetTypeInfo().IsEnum && value is string) {
convertedValue = Enum.Parse(p.PropertyType, (string)value);
} else {
convertedValue = Convert.ChangeType(value, p.PropertyType);
}
p.SetValue(this, convertedValue);
} catch (Exception e) {
throw new ArgumentException("Couldn't set " + keyword, keyword, e);
}
}
}
///
/// Removes the entry with the specified key from the DbConnectionStringBuilder instance.
///
/// The key of the key/value pair to be removed from the connection string in this DbConnectionStringBuilder.
/// true if the key existed within the connection string and was removed; false if the key did not exist.
public override bool Remove(string keyword)
{
var p = GetProperty(keyword);
var removed = base.ContainsKey(p.Name);
// Note that string property setters call SetValue, which itself calls base.Remove().
p.SetValue(this, PropertyDefaults[p]);
base.Remove(p.Name);
return removed;
}
///
/// Clears the contents of the instance.
///
public override void Clear()
{
Contract.Assert(Keys != null);
foreach (var k in Keys.Cast().ToArray()) {
Remove(k);
}
}
///
/// Determines whether the contains a specific key.
///
/// The key to locate in the .
/// true if the contains an entry with the specified key; otherwise false.
public override bool ContainsKey(string keyword)
{
if (keyword == null)
throw new ArgumentNullException(nameof(keyword));
Contract.EndContractBlock();
return PropertiesByKeyword.ContainsKey(keyword.ToUpperInvariant());
}
PropertyInfo GetProperty(string keyword)
{
PropertyInfo p;
if (!PropertiesByKeyword.TryGetValue(keyword.ToUpperInvariant(), out p)) {
throw new ArgumentException("Keyword not supported: " + keyword, nameof(keyword));
}
return p;
}
///
/// Retrieves a value corresponding to the supplied key from this .
///
/// The key of the item to retrieve.
/// The value corresponding to the key.
/// true if keyword was found within the connection string, false otherwise.
public override bool TryGetValue(string keyword, [CanBeNull] out object value)
{
if (keyword == null)
throw new ArgumentNullException(nameof(keyword));
Contract.EndContractBlock();
PropertyInfo p;
if (!PropertiesByKeyword.TryGetValue(keyword.ToUpperInvariant(), out p))
{
value = null;
return false;
}
value = GetProperty(keyword).GetValue(this) ?? "";
return true;
}
void SetValue(string propertyName, [CanBeNull] object value)
{
var canonicalKeyword = PropertyNameToCanonicalKeyword[propertyName];
if (value == null) {
base.Remove(canonicalKeyword);
} else {
base[canonicalKeyword] = value;
}
}
#endregion
#region Properties - Connection
///
/// The hostname or IP address of the PostgreSQL server to connect to.
///
[Category("Connection")]
[Description("The hostname or IP address of the PostgreSQL server to connect to.")]
[DisplayName("Host")]
[NpgsqlConnectionStringProperty("Server")]
public string Host
{
get { return _host; }
set
{
_host = value;
SetValue(nameof(Host), value);
}
}
string _host;
///
/// The TCP/IP port of the PostgreSQL server.
///
[Category("Connection")]
[Description("The TCP port of the PostgreSQL server.")]
[DisplayName("Port")]
[NpgsqlConnectionStringProperty]
[DefaultValue(NpgsqlConnection.DefaultPort)]
public int Port
{
get { return _port; }
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value), value, "Invalid port: " + value);
Contract.EndContractBlock();
_port = value;
SetValue(nameof(Port), value);
}
}
int _port;
///
/// The PostgreSQL database to connect to.
///
[Category("Connection")]
[Description("The PostgreSQL database to connect to.")]
[DisplayName("Database")]
[NpgsqlConnectionStringProperty("DB")]
public string Database
{
get { return _database; }
set
{
_database = value;
SetValue(nameof(Database), value);
}
}
string _database;
///
/// The username to connect with. Not required if using IntegratedSecurity.
///
[Category("Connection")]
[Description("The username to connect with. Not required if using IntegratedSecurity.")]
[DisplayName("Username")]
[NpgsqlConnectionStringProperty("User Name", "UserId", "User Id", "UID")]
public string Username
{
get
{
#if NET45 || NET451 || DNX451
if ((_integratedSecurity) && (String.IsNullOrEmpty(_username))) {
_username = GetIntegratedUserName();
}
#endif
return _username;
}
set
{
_username = value;
SetValue(nameof(Username), value);
}
}
string _username;
///
/// The password to connect with. Not required if using IntegratedSecurity.
///
[Category("Connection")]
[Description("The password to connect with. Not required if using IntegratedSecurity.")]
[PasswordPropertyText(true)]
[DisplayName("Password")]
[NpgsqlConnectionStringProperty("PSW", "PWD")]
public string Password
{
get { return _password; }
set
{
_password = value;
SetValue(nameof(Password), value);
}
}
string _password;
///
/// The optional application name parameter to be sent to the backend during connection initiation.
///
[Category("Connection")]
[Description("The optional application name parameter to be sent to the backend during connection initiation")]
[DisplayName("Application Name")]
[NpgsqlConnectionStringProperty]
public string ApplicationName
{
get { return _applicationName; }
set
{
_applicationName = value;
SetValue(nameof(ApplicationName), value);
}
}
string _applicationName;
///
/// Whether to enlist in an ambient TransactionScope.
///
[Category("Connection")]
[Description("Whether to enlist in an ambient TransactionScope.")]
[DisplayName("Enlist")]
[NpgsqlConnectionStringProperty]
public bool Enlist
{
get { return _enlist; }
set
{
_enlist = value;
SetValue(nameof(Enlist), value);
}
}
bool _enlist;
///
/// Gets or sets the schema search path.
///
[Category("Connection")]
[Description("Gets or sets the schema search path.")]
[DisplayName("Search Path")]
[NpgsqlConnectionStringProperty]
public string SearchPath
{
get { return _searchpath; }
set
{
_searchpath = value;
SetValue(nameof(SearchPath), value);
}
}
string _searchpath;
#endregion
#region Properties - Security
///
/// Controls whether SSL is required, disabled or preferred, depending on server support.
///
[Category("Security")]
[Description("Controls whether SSL is required, disabled or preferred, depending on server support.")]
[DisplayName("SSL Mode")]
[NpgsqlConnectionStringProperty]
public SslMode SslMode
{
get { return _sslmode; }
set
{
_sslmode = value;
SetValue(nameof(SslMode), value);
}
}
SslMode _sslmode;
///
/// Whether to trust the server certificate without validating it.
///
[Category("Security")]
[Description("Whether to trust the server certificate without validating it.")]
[DisplayName("Trust Server Certificate")]
[NpgsqlConnectionStringProperty]
public bool TrustServerCertificate
{
get { return _trustServerCertificate; }
set
{
_trustServerCertificate = value;
SetValue(nameof(TrustServerCertificate), value);
}
}
bool _trustServerCertificate;
///
/// Npgsql uses its own internal implementation of TLS/SSL. Turn this on to use .NET SslStream instead.
///
[Category("Security")]
[Description("Npgsql uses its own internal implementation of TLS/SSL. Turn this on to use .NET SslStream instead.")]
[DisplayName("Use SSL Stream")]
[NpgsqlConnectionStringProperty]
public bool UseSslStream
{
get { return _useSslStream; }
set
{
_useSslStream = value;
SetValue(nameof(UseSslStream), value);
}
}
bool _useSslStream;
///
/// Whether to use Windows integrated security to log in.
///
[Category("Security")]
[Description("Whether to use Windows integrated security to log in.")]
[DisplayName("Integrated Security")]
[NpgsqlConnectionStringProperty]
public bool IntegratedSecurity
{
get { return _integratedSecurity; }
set
{
#if !NET40
if (value)
CheckIntegratedSecuritySupport();
#endif
_integratedSecurity = value;
SetValue(nameof(IntegratedSecurity), value);
}
}
bool _integratedSecurity;
///
/// The Kerberos service name to be used for authentication.
///
[Category("Security")]
[Description("The Kerberos service name to be used for authentication.")]
[DisplayName("Kerberos Service Name")]
[NpgsqlConnectionStringProperty("Krbsrvname")]
public string KerberosServiceName
{
get { return _kerberosServiceName; }
set
{
_kerberosServiceName = value;
SetValue(nameof(KerberosServiceName), value);
}
}
string _kerberosServiceName;
///
/// The Kerberos realm to be used for authentication.
///
[Category("Security")]
[Description("The Kerberos realm to be used for authentication.")]
[DisplayName("Include Realm")]
[NpgsqlConnectionStringProperty]
public bool IncludeRealm
{
get { return _includeRealm; }
set
{
_includeRealm = value;
SetValue(nameof(IncludeRealm), value);
}
}
bool _includeRealm;
///
/// Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state.
///
[Category("Security")]
[Description("Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state.")]
[DisplayName("Persist Security Info")]
[NpgsqlConnectionStringProperty]
public bool PersistSecurityInfo
{
get { return _persistSecurityInfo; }
set
{
_persistSecurityInfo = value;
SetValue(nameof(PersistSecurityInfo), value);
}
}
bool _persistSecurityInfo;
#endregion
#region Properties - Pooling
///
/// Whether connection pooling should be used.
///
[Category("Pooling")]
[Description("Whether connection pooling should be used.")]
[DisplayName("Pooling")]
[NpgsqlConnectionStringProperty]
[DefaultValue(true)]
public bool Pooling
{
get { return _pooling; }
set
{
_pooling = value;
SetValue(nameof(Pooling), value);
}
}
bool _pooling;
///
/// The minimum connection pool size.
///
[Category("Pooling")]
[Description("The minimum connection pool size.")]
[DisplayName("Minimum Pool Size")]
[NpgsqlConnectionStringProperty]
[DefaultValue(1)]
public int MinPoolSize
{
get { return _minPoolSize; }
set
{
if (value < 0 || value > NpgsqlConnectorPool.PoolSizeLimit)
throw new ArgumentOutOfRangeException(nameof(value), value, "MinPoolSize must be between 0 and " + NpgsqlConnectorPool.PoolSizeLimit);
Contract.EndContractBlock();
_minPoolSize = value;
SetValue(nameof(MinPoolSize), value);
}
}
int _minPoolSize;
///
/// The maximum connection pool size.
///
[Category("Pooling")]
[Description("The maximum connection pool size.")]
[DisplayName("Maximum Pool Size")]
[NpgsqlConnectionStringProperty]
[DefaultValue(20)]
public int MaxPoolSize
{
get { return _maxPoolSize; }
set
{
if (value < 0 || value > NpgsqlConnectorPool.PoolSizeLimit)
throw new ArgumentOutOfRangeException(nameof(value), value, "MaxPoolSize must be between 0 and " + NpgsqlConnectorPool.PoolSizeLimit);
Contract.EndContractBlock();
_maxPoolSize = value;
SetValue(nameof(MaxPoolSize), value);
}
}
int _maxPoolSize;
///
/// The time to wait before closing unused connections in the pool if the count
/// of all connections exeeds MinPoolSize.
///
///
/// If connection pool contains unused connections for ConnectionLifeTime seconds,
/// the half of them will be closed. If there will be unused connections in a second
/// later then again the half of them will be closed and so on.
/// This strategy provide smooth change of connection count in the pool.
///
/// The time (in seconds) to wait. The default value is 15 seconds.
[Category("Pooling")]
[Description("The time to wait before closing unused connections in the pool if the count of all connections exeeds MinPoolSize.")]
[DisplayName("Connection Lifetime")]
[NpgsqlConnectionStringProperty]
[DefaultValue(15)]
public int ConnectionLifeTime
{
get { return _connectionLifeTime; }
set
{
_connectionLifeTime = value;
SetValue(nameof(ConnectionLifeTime), value);
}
}
int _connectionLifeTime;
#endregion
#region Properties - Timeouts
///
/// The time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error.
/// Defaults to 15 seconds.
///
[Category("Timeouts")]
[Description("The time to wait (in seconds) while trying to establish a connection before terminating the attempt and generating an error.")]
[DisplayName("Timeout")]
[NpgsqlConnectionStringProperty]
[DefaultValue(15)]
public int Timeout
{
get { return _timeout; }
set
{
if (value < 0 || value > NpgsqlConnection.TimeoutLimit)
throw new ArgumentOutOfRangeException(nameof(value), value, "Timeout must be between 0 and " + NpgsqlConnection.TimeoutLimit);
Contract.EndContractBlock();
_timeout = value;
SetValue(nameof(Timeout), value);
}
}
int _timeout;
///
/// The time to wait (in seconds) while trying to execute a command before terminating the attempt and generating an error.
/// Defaults to 30 seconds.
///
[Category("Timeouts")]
[Description("The time to wait (in seconds) while trying to execute a command before terminating the attempt and generating an error. Set to zero for infinity.")]
[DisplayName("Command Timeout")]
[NpgsqlConnectionStringProperty]
[DefaultValue(NpgsqlCommand.DefaultTimeout)]
public int CommandTimeout
{
get { return _commandTimeout; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), value, "CommandTimeout can't be negative");
Contract.EndContractBlock();
_commandTimeout = value;
SetValue(nameof(CommandTimeout), value);
}
}
int _commandTimeout;
///
/// The time to wait (in seconds) while trying to execute a an internal command before terminating the attempt and generating an error.
///
[Category("Timeouts")]
[Description("The time to wait (in seconds) while trying to execute a an internal command before terminating the attempt and generating an error. -1 uses CommandTimeout, 0 means no timeout.")]
[DisplayName("Internal Command Timeout")]
[NpgsqlConnectionStringProperty]
[DefaultValue(-1)]
public int InternalCommandTimeout
{
get { return _internalCommandTimeout; }
set
{
if (value != 0 && value != -1 && value < NpgsqlConnector.MinimumInternalCommandTimeout)
throw new ArgumentOutOfRangeException(nameof(value), value,
$"InternalCommandTimeout must be >= {NpgsqlConnector.MinimumInternalCommandTimeout}, 0 (infinite) or -1 (use CommandTimeout)");
Contract.EndContractBlock();
_internalCommandTimeout = value;
SetValue(nameof(InternalCommandTimeout), value);
}
}
int _internalCommandTimeout;
///
/// Whether to have the backend enforce and
/// via the statement_timeout variable. Defaults to true.
///
[Category("Timeouts")]
[Description("Whether to have the backend enforce CommandTimeout and InternalCommandTimeout via the statement_timeout variable.")]
[DisplayName("Backend Timeouts")]
[NpgsqlConnectionStringProperty]
[DefaultValue(true)]
public bool BackendTimeouts
{
get { return _backendTimeouts; }
set
{
_backendTimeouts = value;
SetValue(nameof(BackendTimeouts), value);
}
}
bool _backendTimeouts;
#endregion
#region Properties - Entity Framework
///
/// The database template to specify when creating a database in Entity Framework. If not specified,
/// PostgreSQL defaults to "template1".
///
///
/// http://www.postgresql.org/docs/current/static/manage-ag-templatedbs.html
///
[Category("Entity Framework")]
[Description("The database template to specify when creating a database in Entity Framework. If not specified, PostgreSQL defaults to \"template1\".")]
[DisplayName("EF Template Database")]
[NpgsqlConnectionStringProperty]
public string EntityTemplateDatabase
{
get { return _entityTemplateDatabase; }
set
{
_entityTemplateDatabase = value;
SetValue(nameof(EntityTemplateDatabase), value);
}
}
string _entityTemplateDatabase;
///
/// The database admin to specify when creating and dropping a database in Entity Framework. This is needed because
/// Npgsql needs to connect to a database in order to send the create/drop database command.
/// If not specified, defaults to "template1". Check NpgsqlServices.UsingPostgresDBConnection for more information.
///
[Category("Entity Framework")]
[Description("The database admin to specify when creating and dropping a database in Entity Framework. If not specified, defaults to \"template1\".")]
[DisplayName("EF Admin Database")]
[NpgsqlConnectionStringProperty]
public string EntityAdminDatabase
{
get { return _entityAdminDatabase; }
set
{
_entityAdminDatabase = value;
SetValue(nameof(EntityAdminDatabase), value);
}
}
string _entityAdminDatabase;
#endregion
#region Properties - Advanced
///
/// Whether to process messages that arrive between command activity.
///
[Category("Advanced")]
[Description("Whether to process messages that arrive between command activity.")]
[DisplayName("Continuous Processing")]
[NpgsqlConnectionStringProperty("SyncNotification")]
public bool ContinuousProcessing
{
get { return _continuousProcessing; }
set
{
_continuousProcessing = value;
SetValue(nameof(ContinuousProcessing), value);
}
}
bool _continuousProcessing;
///
/// The number of seconds of connection inactivity before Npgsql sends a keepalive query.
/// Set to 0 (the default) to disable.
///
[Category("Advanced")]
[Description("The number of seconds of connection inactivity before Npgsql sends a keepalive query.")]
[DisplayName("Keepalive")]
[NpgsqlConnectionStringProperty]
public int KeepAlive
{
get { return _keepAlive; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), value, "KeepAlive can't be negative");
Contract.EndContractBlock();
_keepAlive = value;
SetValue(nameof(KeepAlive), value);
}
}
int _keepAlive;
///
/// Gets or sets the buffer size.
///
[Category("Advanced")]
[Description("Determines the size of the internal buffer Npgsql uses when reading or writing. Increasing may improve performance if transferring large values from the database.")]
[DisplayName("Buffer Size")]
[NpgsqlConnectionStringProperty]
[DefaultValue(NpgsqlBuffer.DefaultBufferSize)]
public int BufferSize
{
get { return _bufferSize; }
set
{
_bufferSize = value;
SetValue(nameof(BufferSize), value);
}
}
int _bufferSize;
#endregion
#region Properties - Compatibility
///
/// A compatibility mode for special PostgreSQL server types.
///
[Category("Compatibility")]
[Description("A compatibility mode for special PostgreSQL server types.")]
[DisplayName("Server Compatibility Mode")]
[NpgsqlConnectionStringProperty]
public ServerCompatibilityMode ServerCompatibilityMode
{
get { return _serverCompatibilityMode; }
set
{
_serverCompatibilityMode = value;
SetValue(nameof(ServerCompatibilityMode), value);
}
}
ServerCompatibilityMode _serverCompatibilityMode;
///
/// Makes MaxValue and MinValue timestamps and dates readable as infinity and negative infinity.
///
[Category("Compatibility")]
[Description("Makes MaxValue and MinValue timestamps and dates readable as infinity and negative infinity.")]
[DisplayName("Convert Infinity DateTime")]
[NpgsqlConnectionStringProperty]
public bool ConvertInfinityDateTime
{
get { return _convertInfinityDateTime; }
set
{
_convertInfinityDateTime = value;
SetValue(nameof(ConvertInfinityDateTime), value);
}
}
bool _convertInfinityDateTime;
#endregion
#region Properties - Obsolete
///
/// Obsolete, see https://github.com/npgsql/Npgsql/wiki/PreloadReader-Removal
///
[Category("Obsolete")]
[Description("Obsolete, see https://github.com/npgsql/Npgsql/wiki/PreloadReader-Removal")]
[DisplayName("Preload Reader")]
[NpgsqlConnectionStringProperty]
[Obsolete]
public bool PreloadReader
{
get { return false; }
set { throw new NotSupportedException("The PreloadReader parameter is no longer supported. Please see https://github.com/npgsql/Npgsql/wiki/PreloadReader-Removal"); }
}
///
/// Obsolete, see https://github.com/npgsql/Npgsql/wiki/UseExtendedTypes-Removal
///
[Category("Obsolete")]
[Description("Obsolete, see https://github.com/npgsql/Npgsql/wiki/UseExtendedTypes-Removal")]
[DisplayName("Use Extended Types")]
[NpgsqlConnectionStringProperty]
[Obsolete]
public bool UseExtendedTypes
{
get { return false; }
set { throw new NotSupportedException("The UseExtendedTypes parameter is no longer supported. Please see https://github.com/npgsql/Npgsql/wiki/UseExtendedTypes-Removal"); }
}
#endregion
#region Integrated security support
///
/// No integrated security if we're on mono and .NET 4.5 because of ClaimsIdentity,
/// see https://github.com/npgsql/Npgsql/issues/133
///
static void CheckIntegratedSecuritySupport()
{
if (Type.GetType("Mono.Runtime") != null)
throw new NotSupportedException("IntegratedSecurity is currently unsupported on mono and .NET 4.5 (see https://github.com/npgsql/Npgsql/issues/133)");
}
#if NET45 || NET451 || DNX451
class CachedUpn
{
public string Upn;
public DateTime ExpiryTimeUtc;
}
static readonly Dictionary CachedUpns = new Dictionary();
private string GetWindowsIdentityUserName()
{
var identity = WindowsIdentity.GetCurrent();
return identity == null ? string.Empty : identity.Name.Split('\\')[1];
}
[CanBeNull]
string GetIntegratedUserName()
{
// Side note: This maintains the hack fix mentioned before for https://github.com/npgsql/Npgsql/issues/133.
// In a nutshell, starting with .NET 4.5 WindowsIdentity inherits from ClaimsIdentity
// which doesn't exist in mono, and calling a WindowsIdentity method bombs.
// The workaround is that this function that actually deals with WindowsIdentity never
// gets called on mono, so never gets JITted and the problem goes away.
// Gets the current user's username for integrated security purposes
var identity = WindowsIdentity.GetCurrent();
if (identity?.User == null) {
return null;
}
CachedUpn cachedUpn;
string upn = null;
// Check to see if we already have this UPN cached
lock (CachedUpns) {
if (CachedUpns.TryGetValue(identity.User, out cachedUpn)) {
if (cachedUpn.ExpiryTimeUtc > DateTime.UtcNow)
upn = cachedUpn.Upn;
else
CachedUpns.Remove(identity.User);
}
}
try {
if (upn == null) {
// Try to get the user's UPN in its correct case; this is what the
// server will need to verify against a Kerberos/SSPI ticket
// If the computer does not belong to a domain, returns Empty.
string domainName = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;
if (domainName.Equals(string.Empty))
{
return GetWindowsIdentityUserName();
}
// First, find a domain server we can talk to
string domainHostName;
using (DirectoryEntry rootDse = new DirectoryEntry("LDAP://rootDSE") { AuthenticationType = AuthenticationTypes.Secure }) {
domainHostName = (string)rootDse.Properties["dnsHostName"].Value;
}
// Query the domain server by the current user's SID
using (DirectoryEntry entry = new DirectoryEntry("LDAP://" + domainHostName) { AuthenticationType = AuthenticationTypes.Secure }) {
DirectorySearcher search = new DirectorySearcher(entry,
"(objectSid=" + identity.User.Value + ")", new[] { "userPrincipalName" });
SearchResult result = search.FindOne();
upn = (string)result.Properties["userPrincipalName"][0];
}
}
if (cachedUpn == null) {
// Save this value
cachedUpn = new CachedUpn() { Upn = upn, ExpiryTimeUtc = DateTime.UtcNow.AddHours(3.0) };
lock (CachedUpns) {
CachedUpns[identity.User] = cachedUpn;
}
}
string[] upnParts = upn.Split('@');
if (_includeRealm) {
// Make it Kerberos-y by uppercasing the realm part
return upnParts[0] + "@" + upnParts[1].ToUpperInvariant();
} else {
return upnParts[0];
}
} catch {
// Querying the directory failed, so return the SAM name
// (which probably won't work, but it's better than nothing)
return GetWindowsIdentityUserName();
}
}
#endif
#endregion
#region Misc
internal NpgsqlConnectionStringBuilder Clone()
{
return new NpgsqlConnectionStringBuilder(ConnectionString);
}
#endregion
#region Attributes
[AttributeUsage(AttributeTargets.Property)]
[MeansImplicitUse]
class NpgsqlConnectionStringPropertyAttribute : Attribute
{
internal string[] Aliases { get; }
internal NpgsqlConnectionStringPropertyAttribute()
{
Aliases = Empty;
}
internal NpgsqlConnectionStringPropertyAttribute(params string[] aliases)
{
Aliases = aliases;
}
}
#endregion
}
#region Enums
///
/// An option specified in the connection string that activates special compatibility features.
///
[PublicAPI]
public enum ServerCompatibilityMode
{
///
/// No special server compatibility mode is active
///
None,
///
/// The server is an Amazon Redshift instance.
///
Redshift,
}
///
/// Specifies how to manage SSL.
///
[PublicAPI]
public enum SslMode
{
///
/// SSL is disabled. If the server requires SSL, the connection will fail.
///
Disable,
///
/// Prefer SSL connections if the server allows them, but allow connections without SSL.
///
Prefer,
///
/// Fail the connection if the server doesn't suppotr SSL.
///
Require,
}
#endregion
}