2004-10-14 Umadevi S <sumadevi@novell.com>
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlConnection.cs
1 //
2 // System.Data.SqlClient.SqlConnection.cs
3 //
4 // Authors:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //   Phillip Jerkins (Phillip.Jerkins@morgankeegan.com)
9 //   Diego Caravana (diego@toth.it)
10 //
11 // Copyright (C) Ximian, Inc 2002
12 // Copyright (C) Daniel Morgan 2002, 2003
13 // Copyright (C) Tim Coleman, 2002, 2003
14 // Copyright (C) Phillip Jerkins, 2003
15 //
16
17 //
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
19 //
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 // 
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 // 
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 //
39
40 using Mono.Data.Tds;
41 using Mono.Data.Tds.Protocol;
42 using System;
43 using System.Collections;
44 using System.Collections.Specialized;
45 using System.ComponentModel;
46 using System.Data;
47 using System.Data.Common;
48 using System.EnterpriseServices;
49 using System.Globalization;
50 using System.Net;
51 using System.Net.Sockets;
52 using System.Text;
53 using System.Xml;
54
55 namespace System.Data.SqlClient {
56         [DefaultEvent ("InfoMessage")]
57         public sealed class SqlConnection : Component, IDbConnection, ICloneable        
58         {
59                 #region Fields
60                 bool disposed = false;
61
62                 // The set of SQL connection pools
63                 static TdsConnectionPoolManager sqlConnectionPools = new TdsConnectionPoolManager (TdsVersion.tds70);
64
65                 // The current connection pool
66                 TdsConnectionPool pool;
67
68                 // The connection string that identifies this connection
69                 string connectionString = null;
70
71                 // The transaction object for the current transaction
72                 SqlTransaction transaction = null;
73
74                 // Connection parameters
75                 TdsConnectionParameters parms = new TdsConnectionParameters ();
76                 bool connectionReset;
77                 bool pooling;
78                 string dataSource;
79                 int connectionTimeout;
80                 int minPoolSize;
81                 int maxPoolSize;
82                 int packetSize;
83                 int port = 1433;
84
85                 // The current state
86                 ConnectionState state = ConnectionState.Closed;
87
88                 SqlDataReader dataReader = null;
89                 XmlReader xmlReader = null;
90
91                 // The TDS object
92                 ITds tds;
93
94                 #endregion // Fields
95
96                 #region Constructors
97
98                 public SqlConnection () 
99                         : this (String.Empty)
100                 {
101                 }
102         
103                 public SqlConnection (string connectionString) 
104                 {
105                         ConnectionString = connectionString;
106                 }
107
108                 #endregion // Constructors
109
110                 #region Properties
111
112                 [DataCategory ("Data")]
113                 [DataSysDescription ("Information used to connect to a DataSource, such as 'Data Source=x;Initial Catalog=x;Integrated Security=SSPI'.")]
114                 [DefaultValue ("")]
115                 [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
116                 [RecommendedAsConfigurable (true)]      
117                 [RefreshProperties (RefreshProperties.All)]
118                 [MonoTODO("persist security info, encrypt, enlist and , attachdbfilename keyword not implemented")]
119                 public string ConnectionString  {
120                         get { return connectionString; }
121                         set { SetConnectionString (value); }
122                 }
123         
124                 [DataSysDescription ("Current connection timeout value, 'Connect Timeout=X' in the ConnectionString.")] 
125                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
126                 public int ConnectionTimeout {
127                         get { return connectionTimeout; }
128                 }
129
130                 [DataSysDescription ("Current SQL Server database, 'Initial Catalog=X' in the ConnectionString.")]
131                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
132                 public string Database  {
133                         get { return tds.Database; }
134                 }
135                 
136                 internal SqlDataReader DataReader {
137                         get { return dataReader; }
138                         set { dataReader = value; }
139                 }
140
141                 [DataSysDescription ("Current SqlServer that the connection is opened to, 'Data Source=X' in the ConnectionString.")]   
142                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
143                 public string DataSource {
144                         get { return dataSource; }
145                 }
146
147                 [DataSysDescription ("Network packet size, 'Packet Size=x' in the ConnectionString.")]  
148                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
149                 public int PacketSize {
150                         get { return packetSize; }
151                 }
152
153                 [Browsable (false)]
154                 [DataSysDescription ("Version of the SQL Server accessed by the SqlConnection.")]
155                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
156                 public string ServerVersion {
157                         get { return tds.ServerVersion; }
158                 }
159
160                 [Browsable (false)]
161                 [DataSysDescription ("The ConnectionState indicating whether the connection is open or closed.")]
162                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
163                 public ConnectionState State {
164                         get { return state; }
165                 }
166
167                 internal ITds Tds {
168                         get { return tds; }
169                 }
170
171                 internal SqlTransaction Transaction {
172                         get { return transaction; }
173                         set { transaction = value; }
174                 }
175
176                 [DataSysDescription ("Workstation Id, 'Workstation Id=x' in the ConnectionString.")]    
177                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
178                 public string WorkstationId {
179                         get { return parms.Hostname; }
180                 }
181
182                 internal XmlReader XmlReader {
183                         get { return xmlReader; }
184                         set { xmlReader = value; }
185                 }
186
187                 #endregion // Properties
188
189                 #region Events
190
191                 [DataCategory ("InfoMessage")]
192                 [DataSysDescription ("Event triggered when messages arrive from the DataSource.")]
193                 public event SqlInfoMessageEventHandler InfoMessage;
194
195                 [DataCategory ("StateChange")]
196                 [DataSysDescription ("Event triggered when the connection changes state.")]
197                 public event StateChangeEventHandler StateChange;
198                 
199                 #endregion // Events
200
201                 #region Delegates
202
203                 private void ErrorHandler (object sender, TdsInternalErrorMessageEventArgs e)
204                 {
205                         throw new SqlException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SqlClient Data Provider", e.State);
206                 }
207
208                 private void MessageHandler (object sender, TdsInternalInfoMessageEventArgs e)
209                 {
210                         OnSqlInfoMessage (CreateSqlInfoMessageEvent (e.Errors));
211                 }
212
213                 #endregion // Delegates
214
215                 #region Methods
216
217                 public SqlTransaction BeginTransaction ()
218                 {
219                         return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
220                 }
221
222                 public SqlTransaction BeginTransaction (IsolationLevel iso)
223                 {
224                         return BeginTransaction (iso, String.Empty);
225                 }
226
227                 public SqlTransaction BeginTransaction (string transactionName)
228                 {
229                         return BeginTransaction (IsolationLevel.ReadCommitted, transactionName);
230                 }
231
232                 public SqlTransaction BeginTransaction (IsolationLevel iso, string transactionName)
233                 {
234                         if (state == ConnectionState.Closed)
235                                 throw new InvalidOperationException ("The connection is not open.");
236                         if (transaction != null)
237                                 throw new InvalidOperationException ("SqlConnection does not support parallel transactions.");
238
239                         if (iso == IsolationLevel.Chaos)
240                                 throw new ArgumentException ("Invalid IsolationLevel parameter: must be ReadCommitted, ReadUncommitted, RepeatableRead, or Serializable.");
241
242                         string isolevel = String.Empty;
243                         switch (iso) {
244                         case IsolationLevel.ReadCommitted:
245                                 isolevel = "READ COMMITTED";
246                                 break;
247                         case IsolationLevel.ReadUncommitted:
248                                 isolevel = "READ UNCOMMITTED";
249                                 break;
250                         case IsolationLevel.RepeatableRead:
251                                 isolevel = "REPEATABLE READ";
252                                 break;
253                         case IsolationLevel.Serializable:
254                                 isolevel = "SERIALIZABLE";
255                                 break;
256                         }
257
258                         tds.Execute (String.Format ("SET TRANSACTION ISOLATION LEVEL {0};BEGIN TRANSACTION {1}", isolevel, transactionName));
259
260                         transaction = new SqlTransaction (this, iso);
261                         return transaction;
262                 }
263
264                 public void ChangeDatabase (string database) 
265                 {
266                         if (!IsValidDatabaseName (database))
267                                 throw new ArgumentException (String.Format ("The database name {0} is not valid."));
268                         if (state != ConnectionState.Open)
269                                 throw new InvalidOperationException ("The connection is not open.");
270                         tds.Execute (String.Format ("use {0}", database));
271                 }
272
273                 private void ChangeState (ConnectionState currentState)
274                 {
275                         ConnectionState originalState = state;
276                         state = currentState;
277                         OnStateChange (CreateStateChangeEvent (originalState, currentState));
278                 }
279
280                 public void Close () 
281                 {
282                         if (transaction != null && transaction.IsOpen)
283                                 transaction.Rollback ();
284
285                         if (dataReader != null || xmlReader != null) {
286                                 if(tds != null) tds.SkipToEnd ();
287                                 dataReader = null;
288                                 xmlReader = null;
289                         }
290
291                         if (pooling)
292                                 if(pool != null) pool.ReleaseConnection (tds);
293                         else
294                                 if(tds != null) tds.Disconnect ();
295
296                         if(tds != null) {
297                                 tds.TdsErrorMessage -= new TdsInternalErrorMessageEventHandler (ErrorHandler);
298                                 tds.TdsInfoMessage -= new TdsInternalInfoMessageEventHandler (MessageHandler);
299                         }
300
301                         ChangeState (ConnectionState.Closed);
302                 }
303
304                 public SqlCommand CreateCommand () 
305                 {
306                         SqlCommand command = new SqlCommand ();
307                         command.Connection = this;
308                         return command;
309                 }
310                 
311                 private SqlInfoMessageEventArgs CreateSqlInfoMessageEvent (TdsInternalErrorCollection errors)
312                 {
313                         return new SqlInfoMessageEventArgs (errors);
314                 }
315
316                 private StateChangeEventArgs CreateStateChangeEvent (ConnectionState originalState, ConnectionState currentState)
317                 {
318                         return new StateChangeEventArgs (originalState, currentState);
319                 }
320
321                 protected override void Dispose (bool disposing) 
322                 {
323                         if (!disposed) { 
324                                 if (disposing) {
325                                         if (State == ConnectionState.Open) 
326                                                 Close ();
327                                         parms = null;
328                                         dataSource = null;
329                                 }
330                                 base.Dispose (disposing);
331                                 disposed = true;
332                         }
333                 }
334
335                 [MonoTODO ("Not sure what this means at present.")]
336                 public void EnlistDistributedTransaction (ITransaction transaction)
337                 {
338                         throw new NotImplementedException ();
339                 }
340
341                 object ICloneable.Clone ()
342                 {
343                         return new SqlConnection (ConnectionString);
344                 }
345
346                 IDbTransaction IDbConnection.BeginTransaction ()
347                 {
348                         return BeginTransaction ();
349                 }
350
351                 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel iso)
352                 {
353                         return BeginTransaction (iso);
354                 }
355
356                 IDbCommand IDbConnection.CreateCommand ()
357                 {
358                         return CreateCommand ();
359                 }
360
361                 void IDisposable.Dispose ()
362                 {
363                         Dispose (true);
364                         GC.SuppressFinalize (this);
365                 }
366
367                 public void Open () 
368                 {
369                         string serverName = "";
370                         if (connectionString == null)
371                                 throw new InvalidOperationException ("Connection string has not been initialized.");
372
373                         try {
374                                 if (!pooling) {
375                                         if(!ParseDataSource (dataSource, out port, out serverName))
376                                                 throw new SqlException(20, 0, "SQL Server does not exist or access denied.",  17, "ConnectionOpen (Connect()).", dataSource, parms.ApplicationName, 0);
377                                         tds = new Tds70 (serverName, port, PacketSize, ConnectionTimeout);
378                                 }
379                                 else {
380                                         if(!ParseDataSource (dataSource, out port, out serverName))
381                                                 throw new SqlException(20, 0, "SQL Server does not exist or access denied.",  17, "ConnectionOpen (Connect()).", dataSource, parms.ApplicationName, 0);
382                                         
383                                         TdsConnectionInfo info = new TdsConnectionInfo (serverName, port, packetSize, ConnectionTimeout, minPoolSize, maxPoolSize);
384                                         pool = sqlConnectionPools.GetConnectionPool (connectionString, info);
385                                         tds = pool.GetConnection ();
386                                 }
387                         }
388                         catch (TdsTimeoutException e) {
389                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
390                         }
391
392                         tds.TdsErrorMessage += new TdsInternalErrorMessageEventHandler (ErrorHandler);
393                         tds.TdsInfoMessage += new TdsInternalInfoMessageEventHandler (MessageHandler);
394
395                         if (!tds.IsConnected) {
396                                 try {
397                                         tds.Connect (parms);
398                                 }
399                                 catch {
400                                         if (pooling)
401                                                 pool.ReleaseConnection (tds);
402                                         throw;
403                                 }
404                         }
405
406                         /* Not sure ebout removing these 2 lines.
407                          * The command that gets to the sql server is just
408                          * 'sp_reset_connection' and it fails.
409                          * Either remove them definitely or fix it
410                         else if (connectionReset)
411                                 tds.ExecProc ("sp_reset_connection");
412                         */
413                                 
414                         ChangeState (ConnectionState.Open);
415                 }
416
417                 private bool ParseDataSource (string theDataSource, out int thePort, out string theServerName) 
418                 {
419                         theServerName = "";
420                         string theInstanceName = "";
421                         if ((theDataSource == null) || (theServerName == null))
422                                 throw new ArgumentException("Format of initialization string doesnot conform to specifications");
423                                 
424                         thePort = 1433; // default TCP port for SQL Server
425                         bool success = true;
426
427                         int idx = 0;
428                         if ((idx = theDataSource.IndexOf (",")) > -1) {
429                                 theServerName = theDataSource.Substring (0, idx);
430                                 string p = theDataSource.Substring (idx + 1);
431                                 thePort = Int32.Parse (p);
432                         }
433                         else if ((idx = theDataSource.IndexOf ("\\")) > -1) {
434                                 theServerName = theDataSource.Substring (0, idx);
435                                 theInstanceName = theDataSource.Substring (idx + 1);
436                                 // do port discovery via UDP port 1434
437                                 port = DiscoverTcpPortViaSqlMonitor (theServerName, theInstanceName);
438                                 if (port == -1)
439                                         success = false;
440                         }
441                         else {
442                                 theServerName = theDataSource;
443                         }
444
445                         if(theServerName.Equals("(local)"))
446                                 theServerName = "localhost";
447
448                         return success;
449                 }
450
451                 private bool ConvertIntegratedSecurity (string value)
452                 {
453                         if (value.ToUpper() == "SSPI") 
454                         {
455                                 return true;
456                         }
457
458                         return ConvertToBoolean("integrated security", value);
459                 }
460
461                 private bool ConvertToBoolean(string key, string value)
462                 {
463                         string upperValue = value.ToUpper();
464
465                         if (upperValue == "TRUE" ||upperValue == "YES")
466                         {
467                                 return true;
468                         } 
469                         else if (upperValue == "FALSE" || upperValue == "NO")
470                         {
471                                 return false;
472                         }
473
474                         throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
475                                 "Invalid value \"{0}\" for key '{1}'.", value, key));
476                 }
477
478                 private int ConvertToInt32(string key, string value)
479                 {
480                         try
481                         {
482                                 return int.Parse(value);
483                         }
484                         catch (Exception ex)
485                         {
486                                 throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
487                                         "Invalid value \"{0}\" for key '{1}'.", value, key));
488                         }
489                 }
490
491                 private int DiscoverTcpPortViaSqlMonitor(string ServerName, string InstanceName) 
492                 {
493                         SqlMonitorSocket msock;
494                         msock = new SqlMonitorSocket (ServerName, InstanceName);
495                         int SqlServerPort = msock.DiscoverTcpPort ();
496                         msock = null;
497                         return SqlServerPort;
498                 }
499
500                 void SetConnectionString (string connectionString)
501                 {
502                         NameValueCollection parameters = new NameValueCollection ();
503
504                         if (( connectionString == null)||( connectionString.Length == 0)) 
505                                 return;
506                         connectionString += ";";
507
508                         bool inQuote = false;
509                         bool inDQuote = false;
510                         bool inName = true;
511
512                         string name = String.Empty;
513                         string value = String.Empty;
514                         StringBuilder sb = new StringBuilder ();
515
516                         for (int i = 0; i < connectionString.Length; i += 1) {
517                                 char c = connectionString [i];
518                                 char peek;
519                                 if (i == connectionString.Length - 1)
520                                         peek = '\0';
521                                 else
522                                         peek = connectionString [i + 1];
523
524                                 switch (c) {
525                                 case '\'':
526                                         if (inDQuote)
527                                                 sb.Append (c);
528                                         else if (peek.Equals (c)) {
529                                                 sb.Append (c);
530                                                 i += 1;
531                                         }
532                                         else
533                                                 inQuote = !inQuote;
534                                         break;
535                                 case '"':
536                                         if (inQuote)
537                                                 sb.Append (c);
538                                         else if (peek.Equals (c)) {
539                                                 sb.Append (c);
540                                                 i += 1;
541                                         }
542                                         else
543                                                 inDQuote = !inDQuote;
544                                         break;
545                                 case ';':
546                                         if (inDQuote || inQuote)
547                                                 sb.Append (c);
548                                         else {
549                                                 if (name != String.Empty && name != null) {
550                                                         value = sb.ToString ();
551                                                         parameters [name.ToUpper ().Trim ()] = value.Trim ();
552                                                 }
553                                                 inName = true;
554                                                 name = String.Empty;
555                                                 value = String.Empty;
556                                                 sb = new StringBuilder ();
557                                         }
558                                         break;
559                                 case '=':
560                                         if (inDQuote || inQuote || !inName)
561                                                 sb.Append (c);
562                                         else if (peek.Equals (c)) {
563                                                 sb.Append (c);
564                                                 i += 1;
565                                         }
566                                         else {
567                                                 name = sb.ToString ();
568                                                 sb = new StringBuilder ();
569                                                 inName = false;
570                                         }
571                                         break;
572                                 case ' ':
573                                         if (inQuote || inDQuote)
574                                                 sb.Append (c);
575                                         else if (sb.Length > 0 && !peek.Equals (';'))
576                                                 sb.Append (c);
577                                         break;
578                                 default:
579                                         sb.Append (c);
580                                         break;
581                                 }
582                         }
583
584                         if (this.ConnectionString == null)
585                         {
586                                 SetDefaultConnectionParameters (parameters);
587                         }
588
589                         SetProperties (parameters);
590
591                         this.connectionString = connectionString;
592                 }
593
594                 void SetDefaultConnectionParameters (NameValueCollection parameters)
595                 {
596                         if (null == parameters.Get ("APPLICATION NAME") && null == parameters.Get ("APP"))
597                                 parameters["APPLICATION NAME"] = "Mono SqlClient Data Provider";
598                         if (null == parameters.Get ("TIMEOUT") && null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
599                                 parameters["CONNECT TIMEOUT"] = "15";
600                         if (null == parameters.Get ("CONNECTION LIFETIME"))
601                                 parameters["CONNECTION LIFETIME"] = "0";
602                         if (null == parameters.Get ("CONNECTION RESET"))
603                                 parameters["CONNECTION RESET"] = "true";
604                         if (null == parameters.Get ("ENLIST"))
605                                 parameters["ENLIST"] = "true";
606                         if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
607                                 parameters["INTEGRATED SECURITY"] = "false";
608                         if (null == parameters.Get ("MAX POOL SIZE"))
609                                 parameters["MAX POOL SIZE"] = "100";
610                         if (null == parameters.Get ("MIN POOL SIZE"))
611                                 parameters["MIN POOL SIZE"] = "0";
612                         if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET") && null == parameters.Get ("NETWORK"))
613                                 parameters["NETWORK LIBRARY"] = "dbmssocn";
614                         if (null == parameters.Get ("PACKET SIZE"))
615                                 parameters["PACKET SIZE"] = "512";
616                         if (null == parameters.Get ("PERSIST SECURITY INFO") && null == parameters.Get ("PERSISTSECURITYINFO"))
617                                 parameters["PERSIST SECURITY INFO"] = "false";
618                         if (null == parameters.Get ("POOLING"))
619                                 parameters["POOLING"] = "true";
620                         if (null == parameters.Get ("WORKSTATION ID") && null == parameters.Get ("WSID"))
621                                 parameters["WORKSTATION ID"] = Dns.GetHostName();
622                 }
623
624                 private void SetProperties (NameValueCollection parameters)
625                 {
626                         foreach (string name in parameters) {
627                                 string value = parameters[name];
628
629                                 switch (name) {
630                                         case "APP" :
631                                         case "APPLICATION NAME" :
632                                                 parms.ApplicationName = value;
633                                                 break;
634                                         case "ATTACHDBFILENAME" :
635                                         case "EXTENDED PROPERTIES" :
636                                         case "INITIAL FILE NAME" :
637                                                 throw new NotImplementedException("Attachable database support is not implemented.");
638                                         case "TIMEOUT" :
639                                         case "CONNECT TIMEOUT" :
640                                         case "CONNECTION TIMEOUT" :
641                                                 connectionTimeout = ConvertToInt32 ("connection timeout", value);
642                                                 break;
643                                         case "CONNECTION LIFETIME" :
644                                                 break;
645                                         case "CONNECTION RESET" :
646                                                 connectionReset = ConvertToBoolean ("connection reset", value);
647                                                 break;
648                                         case "LANGUAGE" :
649                                         case "CURRENT LANGUAGE" :
650                                                 parms.Language = value;
651                                                 break;
652                                         case "DATA SOURCE" :
653                                         case "SERVER" :
654                                         case "ADDRESS" :
655                                         case "ADDR" :
656                                         case "NETWORK ADDRESS" :
657                                                 dataSource = value;
658                                                 break;
659                                         case "ENCRYPT":
660                                                 if (ConvertToBoolean("encrypt", value))
661                                                 {
662                                                         throw new NotImplementedException("SSL encryption for"
663                                                                 + " data sent between client and server is not"
664                                                                 + " implemented.");
665                                                 }
666                                                 break;
667                                         case "ENLIST" :
668                                                 if (!ConvertToBoolean("enlist", value))
669                                                 {
670                                                         throw new NotImplementedException("Disabling the automatic"
671                                                                 + " enlistment of connections in the thread's current"
672                                                                 + " transaction context is not implemented.");
673                                                 }
674                                                 break;
675                                         case "INITIAL CATALOG" :
676                                         case "DATABASE" :
677                                                 parms.Database = value;
678                                                 break;
679                                         case "INTEGRATED SECURITY" :
680                                         case "TRUSTED_CONNECTION" :
681                                                 parms.DomainLogin = ConvertIntegratedSecurity(value);
682                                                 break;
683                                         case "MAX POOL SIZE" :
684                                                 maxPoolSize = ConvertToInt32 ("max pool size", value);
685                                                 break;
686                                         case "MIN POOL SIZE" :
687                                                 minPoolSize = ConvertToInt32 ("min pool size", value);
688                                                 break;
689 #if NET_2_0
690                                 case "MULTIPLEACTIVERESULTSETS":
691                                         break;
692 #endif
693                                         case "NET" :
694                                         case "NETWORK" :
695                                         case "NETWORK LIBRARY" :
696                                                 if (!value.ToUpper ().Equals ("DBMSSOCN"))
697                                                         throw new ArgumentException ("Unsupported network library.");
698                                                 break;
699                                         case "PACKET SIZE" :
700                                                 packetSize = ConvertToInt32 ("packet size", value);
701                                                 break;
702                                         case "PASSWORD" :
703                                         case "PWD" :
704                                                 parms.Password = value;
705                                                 break;
706                                         case "PERSISTSECURITYINFO" :
707                                         case "PERSIST SECURITY INFO" :
708                                                 // FIXME : not implemented
709                                                 break;
710                                         case "POOLING" :
711                                                 pooling = ConvertToBoolean("pooling", value);
712                                                 break;
713                                         case "UID" :
714                                         case "USER" :
715                                         case "USER ID" :
716                                                 parms.User = value;
717                                                 break;
718                                         case "WSID" :
719                                         case "WORKSTATION ID" :
720                                                 parms.Hostname = value;
721                                                 break;
722                                         default :
723                                                 throw new ArgumentException("Keyword not supported :"+name);
724                                 }
725                         }
726                 }
727
728                 static bool IsValidDatabaseName (string database)
729                 {
730                         if (database.Length > 32 || database.Length < 1)
731                                 return false;
732
733                         if (database[0] == '"' && database[database.Length] == '"')
734                                 database = database.Substring (1, database.Length - 2);
735                         else if (Char.IsDigit (database[0]))
736                                 return false;
737
738                         if (database[0] == '_')
739                                 return false;
740
741                         foreach (char c in database.Substring (1, database.Length - 1))
742                                 if (!Char.IsLetterOrDigit (c) && c != '_')
743                                         return false;
744                         return true;
745                 }
746
747                 private void OnSqlInfoMessage (SqlInfoMessageEventArgs value)
748                 {
749                         if (InfoMessage != null)
750                                 InfoMessage (this, value);
751                 }
752
753                 private void OnStateChange (StateChangeEventArgs value)
754                 {
755                         if (StateChange != null)
756                                 StateChange (this, value);
757                 }
758
759                 private sealed class SqlMonitorSocket : UdpClient 
760                 {
761                         // UDP port that the SQL Monitor listens
762                         private static readonly int SqlMonitorUdpPort = 1434;
763                         private static readonly string SqlServerNotExist = "SQL Server does not exist or access denied";
764
765                         private string server;
766                         private string instance;
767
768                         internal SqlMonitorSocket (string ServerName, string InstanceName) 
769                                 : base (ServerName, SqlMonitorUdpPort) 
770                         {
771                                 server = ServerName;
772                                 instance = InstanceName;
773                         }
774
775                         internal int DiscoverTcpPort () 
776                         {
777                                 int SqlServerTcpPort;
778                                 Client.Blocking = false;
779                                 // send command to UDP 1434 (SQL Monitor) to get
780                                 // the TCP port to connect to the MS SQL server         
781                                 ASCIIEncoding enc = new ASCIIEncoding ();
782                                 Byte[] rawrq = new Byte [instance.Length + 1];
783                                 rawrq[0] = 4;
784                                 enc.GetBytes (instance, 0, instance.Length, rawrq, 1);
785                                 int bytes = Send (rawrq, rawrq.Length);
786
787                                 if (!Active)
788                                         return -1; // Error
789                                 
790                                 bool result;
791                                 result = Client.Poll (100, SelectMode.SelectRead);
792                                 if (result == false)
793                                         return -1; // Error
794
795                                 if (Client.Available <= 0)
796                                         return -1; // Error
797
798                                 IPEndPoint endpoint = new IPEndPoint (Dns.GetHostByName ("localhost").AddressList [0], 0);
799                                 Byte [] rawrs;
800
801                                 rawrs = Receive (ref endpoint);
802
803                                 string rs = Encoding.ASCII.GetString (rawrs);
804
805                                 string[] rawtokens = rs.Split (';');
806                                 Hashtable data = new Hashtable ();
807                                 for (int i = 0; i < rawtokens.Length / 2 && i < 256; i++) {
808                                         data [rawtokens [i * 2]] = rawtokens [ i * 2 + 1];
809                                 }
810                                 if (!data.ContainsKey ("tcp")) 
811                                         throw new NotImplementedException ("Only TCP/IP is supported.");
812
813                                 SqlServerTcpPort = int.Parse ((string) data ["tcp"]);
814                                 Close ();
815
816                                 return SqlServerTcpPort;
817                         }
818                 }
819
820                 #endregion // Methods
821         }
822 }