diff options
58 files changed, 3523 insertions, 582 deletions
diff --git a/doc/plans b/doc/plans index db6f6dde7c8..f53aecd4e95 100755 --- a/doc/plans +++ b/doc/plans @@ -8,7 +8,7 @@ Currently you can read our plans for <a href="ado-net.html">ADO.NET</a>, <a - href="asp-net">ASP.NET</a>, <a href="java.html">Java</a> and + href="asp-net.html">ASP.NET</a>, <a href="java.html">Java</a> and <a href="winforms.html">WinForms</a>. diff --git a/doc/sqlite b/doc/sqlite index 35ee3056896..6730477202c 100755 --- a/doc/sqlite +++ b/doc/sqlite @@ -82,7 +82,7 @@ { string connectionString = "URI=file:SqliteTest.db"; IDbConnection dbcon; - dbcon = new MySQLConnection(connectionString); + dbcon = new SqliteConnection(connectionString); dbcon.Open(); IDbCommand dbcmd = dbcon.CreateCommand(); // requires a table to be created named employee diff --git a/man/mono.1 b/man/mono.1 index 3c680323dd3..b668b344a33 100644 --- a/man/mono.1 +++ b/man/mono.1 @@ -271,6 +271,8 @@ Visit http://mail.ximian.com/mailman/mono-list for details. .SH WEB SITE Visit: http://www.go-mono.com for details .SH SEE ALSO -.BR mcs(1), mint(1), monodis(1), mono-config(5) +.BR mcs(1), mint(1), monodis(1), mono-config(5). +.PP +For ASP.NET-related documentation, see the xsp(1) manual page diff --git a/mcs/class/ByteFX.Data/AssemblyInfo.cs b/mcs/class/ByteFX.Data/AssemblyInfo.cs index 39aab33df80..07e3da27400 100755 --- a/mcs/class/ByteFX.Data/AssemblyInfo.cs +++ b/mcs/class/ByteFX.Data/AssemblyInfo.cs @@ -26,7 +26,7 @@ using System.Runtime.CompilerServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("0.7.2.*")] +[assembly: AssemblyVersion("0.7.4.*")] // // In order to sign your assembly you must specify a key to use. Refer to the @@ -53,6 +53,6 @@ using System.Runtime.CompilerServices; // (*) Delay Signing is an advanced option - see the Microsoft .NET Framework // documentation for more information on this. // -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("..\\..\\bytefx.snk")] -//[assembly: AssemblyKeyName("")] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("..\\..\\bytefx.snk")] +[assembly: AssemblyKeyName("")] diff --git a/mcs/class/ByteFX.Data/ByteFX.Data.dll.sources b/mcs/class/ByteFX.Data/ByteFX.Data.dll.sources index 9d2af306d9c..305c1b42bad 100644 --- a/mcs/class/ByteFX.Data/ByteFX.Data.dll.sources +++ b/mcs/class/ByteFX.Data/ByteFX.Data.dll.sources @@ -10,6 +10,7 @@ ./Common/SqlCommandTextEditor.cs ./Common/StringUtility.cs ./Common/Version.cs +./Common/MultiHostStream.cs ./mysqlclient/CharSetMap.cs ./mysqlclient/CommandBuilder.cs ./mysqlclient/command.cs diff --git a/mcs/class/ByteFX.Data/ChangeLog.txt b/mcs/class/ByteFX.Data/ChangeLog.txt index acd157dbee2..3a805d686e1 100755 --- a/mcs/class/ByteFX.Data/ChangeLog.txt +++ b/mcs/class/ByteFX.Data/ChangeLog.txt @@ -1,3 +1,39 @@ +11/30/2003 +MySql - Fixed ConnectionSTring editor dialog (thanks marco p (pomarc)) +Common - UserId now supported in connection strings (thanks Jeff Neeley) +MySql - Attempting to create a parameter that is not input throws an exception (thanks Ryan Gregg) + +11/29/2003 +MySql - Added much documentation + +11/26/2003 +MySql - checked in new MultiHostStream capability. Big thanks to Dan Guisinger for this. he originally submitted the code and idea of supporting multiple machines on the connect string. + +11/25/2003 +MySql - Added alot of documentation. Still alot to do. + +11/24/2003 +MySql - Fixed speed issue with 0.73 + +11/14/2003 +MySql - changed to Thread.Sleep(0) in MySqlDataStream to help optimize the case where it doesn't need to wait (thanks Todd German) +11/1/2003 +MySql - Prepopulating the idlepools to MinPoolSize + +10/31/2003 +MySql - Fixed MySqlPool deadlock condition as well as stupid bug where CreateNewPooledConnection + was not ever adding new connections to the pool. + Also fixed MySqlStream.ReadBytes and ReadByte to not use TicksPerSecond which does not appear + to always be right. + (thanks Matthew J. Peddlesden) +MySql - Fix for precision and scale (thanks Matthew J. Peddlesden) + +10/28/2003 +MySql - Added Thread.Sleep(1) to stream reading methods to be more cpu friendly (thanks Sean McGinnis) + +10/24/2003 +MySql - Fixed problem where ExecuteReader would sometime return null (thanks Lloyd Dupont ) + 10/17/2003 MySql - Fixed major bug with null field handling (thanks Naucki) diff --git a/mcs/class/ByteFX.Data/Common/DBConnectionString.cs b/mcs/class/ByteFX.Data/Common/DBConnectionString.cs index 03943266449..2fe01655ad4 100644 --- a/mcs/class/ByteFX.Data/Common/DBConnectionString.cs +++ b/mcs/class/ByteFX.Data/Common/DBConnectionString.cs @@ -119,7 +119,10 @@ namespace ByteFX.Data.Common !persistSecurityInfo) continue; str.AppendFormat("{0}={1};", key, keyValues[key]); } - str.Remove( str.Length-1, 1 ); + + if (str.Length > 0) + str.Remove( str.Length-1, 1 ); + return str.ToString(); } @@ -138,6 +141,7 @@ namespace ByteFX.Data.Common case "username": case "user id": case "user name": + case "userid": username = value; break; @@ -216,6 +220,8 @@ namespace ByteFX.Data.Common } } + keyValues.Clear(); + // now we run through our normalized key-values, splitting on equals for (int y=0; y < x; y++) { diff --git a/mcs/class/ByteFX.Data/Common/DBParametersEditor.cs b/mcs/class/ByteFX.Data/Common/DBParametersEditor.cs index 7e065fad4c7..0a555796100 100644 --- a/mcs/class/ByteFX.Data/Common/DBParametersEditor.cs +++ b/mcs/class/ByteFX.Data/Common/DBParametersEditor.cs @@ -24,7 +24,7 @@ namespace ByteFX.Data.Common /// <summary> /// Summary description for DBParametersEditor. /// </summary> - public class DBParametersEditor : CollectionEditor + internal class DBParametersEditor : CollectionEditor { public DBParametersEditor(Type t) : base(t) { diff --git a/mcs/class/ByteFX.Data/Common/MultiHostStream.cs b/mcs/class/ByteFX.Data/Common/MultiHostStream.cs new file mode 100644 index 00000000000..56a2c8f9534 --- /dev/null +++ b/mcs/class/ByteFX.Data/Common/MultiHostStream.cs @@ -0,0 +1,194 @@ +using System; +using System.Net; +using System.IO; +using System.Collections; +using System.Threading; + +namespace ByteFX.Data.Common +{ + internal enum MultiHostStreamErrorType + { + Connecting, + Reading, + Writing + } + + /// <summary> + /// Summary description for MultiHostStream. + /// </summary> + internal abstract class MultiHostStream : Stream + { + protected Stream stream; + protected int readTimeOut; + protected Exception baseException; + + /// <summary> + /// Constructs a new MultiHostStream object with the given parameters + /// </summary> + /// <param name="hostList"></param> + /// <param name="port"></param> + /// <param name="readTimeOut"></param> + /// <param name="connectTimeOut"></param> + public MultiHostStream(string hostList, int port, int readTimeOut, int connectTimeOut) + { + this.readTimeOut = readTimeOut; + ProcessHosts( hostList, port, connectTimeOut ); + } + + protected abstract void TimeOut(MultiHostStreamErrorType error); + protected abstract void Error(string msg); + protected abstract bool CreateStream( IPAddress ip, string host, int port ); + protected abstract bool DataAvailable + { + get; + } + + private void ProcessHosts( string hostList, int port, int connectTimeOut ) + { + int startTime = Environment.TickCount; + + int toTicks = connectTimeOut * 1000; + // + // Host name can contain multiple hosts, seperated by &. + string [] dnsHosts = hostList.Split('&'); + Hashtable ips = new Hashtable(); + + // + // Each host name specified may contain multiple IP addresses + // Lets look at the DNS entries for each host name + foreach(string h in dnsHosts) + { + IPHostEntry hostAddress = Dns.GetHostByName(h); + foreach (IPAddress addr in hostAddress.AddressList) + ips.Add( addr, hostAddress.HostName ); + } + IPAddress[] keys = new IPAddress[ ips.Count ]; + ips.Keys.CopyTo( keys, 0 ); + + if ((Environment.TickCount - startTime) > toTicks) + { + TimeOut(MultiHostStreamErrorType.Connecting); + return; + } + + // make sure they gave us at least one host + if (ips.Count == 0) + { + Error("You must specify at least one host"); + return; + } + + int index = 0; + // now choose a random server if there are more than one + if (ips.Count > 1) + { + System.Random random = new Random((int)DateTime.Now.Ticks); + index = random.Next(ips.Count-1); + } + + // + // Lets step through our hosts until we get a connection + for (int i=0; i < ips.Count; i++) + { + if ((Environment.TickCount - startTime) > toTicks) + { + TimeOut(MultiHostStreamErrorType.Connecting); + return; + } + if (CreateStream( (IPAddress)keys[i], (string)ips[keys[i]], port )) + return; + } + } + + public override int ReadByte() + { + int start = Environment.TickCount; + int ticks = readTimeOut * 1000; + + while ((Environment.TickCount - start) < ticks) + { + if (DataAvailable) + { + int b = stream.ReadByte(); + return b; + } + else + Thread.Sleep(1); + } + + TimeOut(MultiHostStreamErrorType.Reading); + return -1; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int numToRead = count; + int start = Environment.TickCount; + int ticks = readTimeOut * 1000; + + while (numToRead > 0 && (Environment.TickCount - start) < ticks) + { + if (DataAvailable) + { + int bytes_read = stream.Read( buffer, offset, numToRead); + if (bytes_read == 0) + return (count - numToRead); + offset += bytes_read; + numToRead -= bytes_read; + } + Thread.Sleep(1); + } + + if (numToRead > 0) + TimeOut(MultiHostStreamErrorType.Reading); + return count; + } + + public override bool CanRead + { + get { return stream.CanRead; } + } + + public override bool CanWrite + { + get { return stream.CanWrite; } + } + + public override bool CanSeek + { + get { return stream.CanSeek; } + } + + public override long Length + { + get { return stream.Length; } + } + + public override long Position + { + get { return stream.Position; } + set { stream.Position = value; } + } + + public override void Flush() + { + stream.Flush(); + } + + public override void SetLength(long length) + { + stream.SetLength( length ); + } + + public override void Write(byte[] buffer, int offset, int count) + { + stream.Write( buffer, offset, count ); + } + + public override long Seek( long offset, SeekOrigin origin ) + { + return stream.Seek( offset, origin ); + } + + } +} diff --git a/mcs/class/ByteFX.Data/Common/NamedPipeStream.cs b/mcs/class/ByteFX.Data/Common/NamedPipeStream.cs index 1d38c253ab5..5ebc5755cb8 100755 --- a/mcs/class/ByteFX.Data/Common/NamedPipeStream.cs +++ b/mcs/class/ByteFX.Data/Common/NamedPipeStream.cs @@ -25,7 +25,7 @@ namespace ByteFX.Data.Common /// <summary> /// Summary description for API. /// </summary> - public class NamedPipeStream : Stream + internal class NamedPipeStream : Stream { [DllImport("kernel32.dll", EntryPoint="CreateFile", SetLastError=true)] private static extern IntPtr CreateFile(String lpFileName, diff --git a/mcs/class/ByteFX.Data/Common/Security.cs b/mcs/class/ByteFX.Data/Common/Security.cs index 811f16c3f70..0547e2e1a1b 100644 --- a/mcs/class/ByteFX.Data/Common/Security.cs +++ b/mcs/class/ByteFX.Data/Common/Security.cs @@ -5,7 +5,7 @@ namespace ByteFX.Data.Common /// <summary> /// Summary description for Security. /// </summary> - public class Security + internal class Security { public Security() { diff --git a/mcs/class/ByteFX.Data/Common/SqlCommandTextEditor.cs b/mcs/class/ByteFX.Data/Common/SqlCommandTextEditor.cs index 530bf20be70..7dfa9743518 100644 --- a/mcs/class/ByteFX.Data/Common/SqlCommandTextEditor.cs +++ b/mcs/class/ByteFX.Data/Common/SqlCommandTextEditor.cs @@ -25,7 +25,7 @@ namespace ByteFX.Data.Common /// <summary> /// Summary description for MySqlConnectionDesign. /// </summary> - public class SqlCommandTextEditor : UITypeEditor + internal class SqlCommandTextEditor : UITypeEditor { public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context) { diff --git a/mcs/class/ByteFX.Data/Common/Version.cs b/mcs/class/ByteFX.Data/Common/Version.cs index eba74166ff2..03b72a6a985 100644 --- a/mcs/class/ByteFX.Data/Common/Version.cs +++ b/mcs/class/ByteFX.Data/Common/Version.cs @@ -5,7 +5,7 @@ namespace ByteFX.Data.Common /// <summary> /// Summary description for Version. /// </summary> - public struct Version + internal struct Version { private int major; private int minor; diff --git a/mcs/class/ByteFX.Data/mysqlclient/CommandBuilder.cs b/mcs/class/ByteFX.Data/mysqlclient/CommandBuilder.cs index 5befb5d7ba0..7572a398e4b 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/CommandBuilder.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/CommandBuilder.cs @@ -23,8 +23,9 @@ using System.Text; namespace ByteFX.Data.MySqlClient { /// <summary> - /// Summary description for CommandBuilder. + /// Automatically generates single-table commands used to reconcile changes made to a DataSet with the associated MySQL database. This class cannot be inherited. /// </summary> + /// <include file='docs/MySqlCommandBuilder.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> [ToolboxItem(false)] [System.ComponentModel.DesignerCategory("Code")] public sealed class MySqlCommandBuilder : Component @@ -40,10 +41,16 @@ namespace ByteFX.Data.MySqlClient private MySqlCommand _deleteCmd; #region Constructors + /// <summary> + /// Overloaded. Initializes a new instance of the SqlCommandBuilder class. + /// </summary> public MySqlCommandBuilder() { } + /// <summary> + /// Overloaded. Initializes a new instance of the SqlCommandBuilder class. + /// </summary> public MySqlCommandBuilder( MySqlDataAdapter adapter ) { _adapter = adapter; @@ -90,6 +97,12 @@ namespace ByteFX.Data.MySqlClient #endregion #region Public Methods + /// <summary> + /// Retrieves parameter information from the stored procedure specified in the MySqlCommand and populates the Parameters collection of the specified MySqlCommand object. + /// This method is not currently supported since stored procedures are not available in MySql. + /// </summary> + /// <param name="command">The MySqlCommand referencing the stored procedure from which the parameter information is to be derived. The derived parameters are added to the Parameters collection of the MySqlCommand.</param> + /// <exception cref="InvalidOperationException">The command text is not a valid stored procedure name.</exception> public static void DeriveParameters(MySqlCommand command) { throw new MySqlException("DeriveParameters is not supported (due to MySql not supporting SP)"); diff --git a/mcs/class/ByteFX.Data/mysqlclient/Connection.cs b/mcs/class/ByteFX.Data/mysqlclient/Connection.cs index 6a634886061..47895e40d6e 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/Connection.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/Connection.cs @@ -37,9 +37,12 @@ namespace ByteFX.Data.MySqlClient internal ConnectionState state; private MySqlInternalConnection internalConnection; private MySqlDataReader dataReader; - private NumberFormatInfo numberFormat; - private MySqlConnectionString settings; + private NumberFormatInfo numberFormat; + private MySqlConnectionString settings; + /// <summary> + /// Occurs when the state of the connection changes. + /// </summary> public event StateChangeEventHandler StateChange; @@ -149,17 +152,17 @@ namespace ByteFX.Data.MySqlClient [Browsable(false)] public string ServerVersion { - get { return ""; } //internalConnection.GetServerVersion(); } + get { return internalConnection.Driver.Version; } } internal Encoding Encoding { get { -//TODO if (encoding == null) + if (internalConnection == null) return System.Text.Encoding.Default; -// else -// return encoding; + else + return internalConnection.Driver.Encoding; } } @@ -167,6 +170,7 @@ namespace ByteFX.Data.MySqlClient /// <summary> /// Gets or sets the string used to connect to a MySQL Server database. /// </summary> + /// <include file='docs/MySqlConnection.xml' path='MyDocs/MyMembers[@name="ConnectionString"]/*'/> #if WINDOWS [Editor(typeof(Designers.ConnectionStringEditor), typeof(System.Drawing.Design.UITypeEditor))] #endif @@ -340,7 +344,11 @@ namespace ByteFX.Data.MySqlClient } #region ICloneable - public object Clone() + /// <summary> + /// Creates a new MySqlConnection object with the exact same ConnectionString value + /// </summary> + /// <returns>A cloned MySqlConnection object</returns> + object ICloneable.Clone() { MySqlConnection clone = new MySqlConnection(); clone.ConnectionString = this.ConnectionString; diff --git a/mcs/class/ByteFX.Data/mysqlclient/ConnectionInternal.cs b/mcs/class/ByteFX.Data/mysqlclient/ConnectionInternal.cs index 52b23ce06c1..cb51b1f1ae8 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/ConnectionInternal.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/ConnectionInternal.cs @@ -40,12 +40,13 @@ namespace ByteFX.Data.MySqlClient Packet packet; try { - packet = driver.SendSql( "show status like 'uptime'" ); + byte[] bytes = driver.Encoding.GetBytes("show status like 'uptime'"); + packet = driver.SendSql( bytes ); // we have to read for two last packets since MySql sends // us a last packet after schema and again after rows // I will likely change this later to have the driver just // return schema in one very large packet. - while (packet.Type != PacketType.Last) + while (! packet.IsLastPacket()) packet = driver.ReadPacket(); } catch @@ -74,10 +75,13 @@ namespace ByteFX.Data.MySqlClient if (serverVariablesSet) return; // retrieve the encoding that should be used for character data - MySqlCommand cmd = new MySqlCommand("select @@max_allowed_packet", connection); + MySqlCommand cmd = new MySqlCommand("show variables like 'max_allowed_packet'", connection); try { - driver.MaxPacketSize = Convert.ToInt64(cmd.ExecuteScalar()); + MySqlDataReader reader = cmd.ExecuteReader(); + reader.Read(); + driver.MaxPacketSize = reader.GetInt64( 1 ); + reader.Close(); } catch { @@ -94,7 +98,10 @@ namespace ByteFX.Data.MySqlClient driver.Encoding = CharSetMap.GetEncoding( reader.GetString(1) ); reader.Close(); } - catch { } + catch + { + throw new MySqlException("Failure to initialize connection"); + } serverVariablesSet = true; } @@ -102,8 +109,7 @@ namespace ByteFX.Data.MySqlClient public void Open() { driver = new Driver(); - driver.Open( settings.Host, settings.Port, settings.Username, settings.Password, - settings.UseCompression, settings.ConnectTimeout ); + driver.Open( settings ); createTime = DateTime.Now; } diff --git a/mcs/class/ByteFX.Data/mysqlclient/ConnectionString.cs b/mcs/class/ByteFX.Data/mysqlclient/ConnectionString.cs index ac5dac837fa..f3a9567d794 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/ConnectionString.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/ConnectionString.cs @@ -8,7 +8,7 @@ namespace ByteFX.Data.MySqlClient /// </summary> internal sealed class MySqlConnectionString : DBConnectionString { - private bool useCompression; + private bool useCompression = false; public MySqlConnectionString() { diff --git a/mcs/class/ByteFX.Data/mysqlclient/Driver.cs b/mcs/class/ByteFX.Data/mysqlclient/Driver.cs index 6aa5404c56e..fec7dfea8bc 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/Driver.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/Driver.cs @@ -37,11 +37,11 @@ namespace ByteFX.Data.MySqlClient protected MySqlStream stream; protected Encoding encoding; protected byte packetSeq; - protected int timeOut; protected long maxPacketSize; protected Packet peekedPacket = null; protected ByteFX.Data.Common.Version serverVersion; protected bool isOpen; + protected string versionString; int protocol; uint threadID; @@ -69,41 +69,33 @@ namespace ByteFX.Data.MySqlClient set { maxPacketSize = value; } } - /// <summary> - /// - /// </summary> - /// <param name="host"></param> - /// <param name="port"></param> - /// <param name="userid"></param> - /// <param name="password"></param> - public void Open( String host, int port, String userid, String password, - bool UseCompression, int connectTimeout ) + public string Version + { + get { return versionString; } + } + + public void Open( MySqlConnectionString settings ) { - timeOut = connectTimeout; - stream = new MySqlStream( host, port, timeOut ); + stream = new MySqlStream( settings.Host, settings.Port, 30, settings.ConnectTimeout ); Packet packet = ReadPacket(); // read off the protocol version protocol = packet.ReadByte(); - serverVersion = ByteFX.Data.Common.Version.Parse( packet.ReadString() ); + versionString = packet.ReadString(); + serverVersion = ByteFX.Data.Common.Version.Parse( versionString ); threadID = (uint)packet.ReadInteger(4); encryptionSeed = packet.ReadString(); // read in Server capabilities if they are provided serverCaps = 0; - if (packet.CanRead) + if (packet.HasMoreData) serverCaps = (int)packet.ReadInteger(2); - Authenticate( userid, password, UseCompression ); + Authenticate( settings.Username, settings.Password, settings.UseCompression ); isOpen = true; } - /// <summary> - /// - /// </summary> - /// <param name="userid"></param> - /// <param name="password"></param> private void Authenticate( String userid, String password, bool UseCompression ) { ClientParam clientParam = ClientParam.CLIENT_FOUND_ROWS | ClientParam.CLIENT_LONG_FLAG; @@ -156,7 +148,8 @@ namespace ByteFX.Data.MySqlClient /// <summary> /// AuthenticateSecurity implements the new 4.1 authentication scheme /// </summary> - /// <param name="password"></param> + /// <param name="packet">The in-progress packet we use to complete the authentication</param> + /// <param name="password">The password of the user to use</param> private void AuthenticateSecurely( Packet packet, string password ) { packet.WriteString("xxxxxxxx", encoding ); @@ -169,7 +162,7 @@ namespace ByteFX.Data.MySqlClient SHA1 sha = new SHA1CryptoServiceProvider(); byte[] firstPassBytes = sha.ComputeHash( System.Text.Encoding.Default.GetBytes(newPass)); - byte[] salt = packet.GetBytes(); + byte[] salt = packet.GetBuffer(); byte[] input = new byte[ firstPassBytes.Length + 4 ]; salt.CopyTo( input, 0 ); firstPassBytes.CopyTo( input, 4 ); @@ -183,11 +176,10 @@ namespace ByteFX.Data.MySqlClient // send the packet packet = new Packet(); - packet.WriteBytes( firstPassBytes, 0, 20 ); + packet.Write( firstPassBytes, 0, 20 ); SendPacket(packet); } - /// <summary> /// /// </summary> @@ -272,35 +264,15 @@ namespace ByteFX.Data.MySqlClient Packet packet = ReadRawPacket(); - if (packet.Type == PacketType.Error) + // if this is an error packet, then throw the exception + if (packet[0] == 0xff) { + packet.ReadByte(); int errorCode = (int)packet.ReadInteger(2); string msg = packet.ReadString(); throw new MySqlException( msg, errorCode ); } - else if (packet.Type != PacketType.UpdateOrOk) - packet.Position = 0; - - return packet; - } - - /// <summary> - /// - /// </summary> - /// <param name="packet"></param> - private Packet LoadSchemaIntoPacket( Packet packet, int count ) - { - for (int i=0; i < count; i++) - { - Packet colPacket = ReadRawPacket(); - packet.AppendPacket( colPacket ); - } - Packet lastPacket = ReadRawPacket(); - if (lastPacket.Type != PacketType.Last) - throw new MySqlException("Last packet not received when expected"); - packet.Type = PacketType.ResultSchema; - packet.Position = 0; return packet; } @@ -338,7 +310,7 @@ namespace ByteFX.Data.MySqlClient byte[] compressed_buffer = new byte[packet.Length * 2]; Deflater deflater = new Deflater(); - deflater.SetInput( packet.GetBytes(), 0, packet.Length ); + deflater.SetInput( packet.GetBuffer(), 0, packet.Length ); deflater.Finish(); int comp_len = deflater.Deflate( compressed_buffer, 0, compressed_buffer.Length ); if (comp_len > packet.Length) return null; @@ -368,7 +340,7 @@ namespace ByteFX.Data.MySqlClient header.WriteInteger( packet.Length + HEADER_LEN, 3 ); header.WriteByte( packetSeq ); header.WriteInteger( 0, 3 ); - buffer = packet.GetBytes(); + buffer = packet.GetBuffer(); } // now write the internal header header.WriteInteger( packet.Length, 3 ); @@ -376,16 +348,14 @@ namespace ByteFX.Data.MySqlClient } else { - header = new Packet(); - header.WriteInteger( packet.Length, 3 ); - header.WriteByte( packetSeq ); - buffer = packet.GetBytes(); + buffer = packet.GetBytes( packetSeq ); } packetSeq++; // send the data to eth server - stream.Write( header.GetBytes(), 0, header.Length ); - stream.Write( buffer, 0, buffer.Length ); + int start = 0; + if (! useCompression) start = 3; + stream.Write( buffer, start, packet.Length - start ); stream.Flush(); } @@ -412,7 +382,7 @@ namespace ByteFX.Data.MySqlClient SendPacket(packet); packet = ReadPacket(); - if (packet.Type != PacketType.UpdateOrOk) + if (packet[0] != 0) throw new MySqlException("SendCommand failed for command " + text ); } @@ -423,42 +393,28 @@ namespace ByteFX.Data.MySqlClient /// <returns>A packet containing the bytes returned by the server</returns> public Packet SendQuery( byte[] sql ) { + string s = encoding.GetString(sql); + Packet packet = new Packet(); packetSeq = 0; packet.WriteByte( (byte)DBCmd.QUERY ); - packet.WriteBytes( sql, 0, sql.Length ); + packet.Write( sql, 0, sql.Length ); SendPacket( packet ); return ReadPacket(); } - public Packet SendSql( string sql ) - { - byte[] bytes = encoding.GetBytes(sql); + + public Packet SendSql( byte[] bytes ) + { Packet packet = new Packet(); packetSeq = 0; packet.WriteByte( (byte)DBCmd.QUERY ); - packet.WriteBytes( bytes, 0, bytes.Length ); + packet.Write( bytes, 0, bytes.Length ); SendPacket( packet ); packet = ReadPacket(); - - switch (packet.Type) - { - case PacketType.LoadDataLocal: - SendFileToServer(); - return null; - - case PacketType.Other: - packet.Position = 0; - int count = (int)packet.ReadLenInteger(); - if (count > 0) - return LoadSchemaIntoPacket( packet, count ); - else - return packet; - } - return packet; } @@ -472,10 +428,11 @@ namespace ByteFX.Data.MySqlClient } /// <summary> - /// + /// Encrypts a password using the MySql encryption scheme /// </summary> - /// <param name="password"></param> - /// <param name="seed"></param> + /// <param name="password">The password to encrypt</param> + /// <param name="message">The encryption seed the server gave us</param> + /// <param name="new_ver">Indicates if we should use the old or new encryption scheme</param> /// <returns></returns> public static String EncryptPassword(String password, String message, bool new_ver) { diff --git a/mcs/class/ByteFX.Data/mysqlclient/Exception.cs b/mcs/class/ByteFX.Data/mysqlclient/Exception.cs index 9e4b315f617..2e8efbc8820 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/Exception.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/Exception.cs @@ -21,25 +21,30 @@ using System.Runtime.Serialization; namespace ByteFX.Data.MySqlClient { /// <summary> - /// Summary description for MySqlException. + /// The exception that is thrown when MySQL returns an error. This class cannot be inherited. /// </summary> + /// <include file='docs/MySqlException.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> [Serializable] - public sealed class MySqlException : Exception + public sealed class MySqlException : SystemException { - public MySqlException(string msg) : base(msg) + internal MySqlException(string msg) : base(msg) + { + } + + internal MySqlException( string msg, Exception ex ) : base(msg, ex) { } - public MySqlException() + internal MySqlException() { } - public MySqlException(string msg, int errno) : base(msg) + internal MySqlException(string msg, int errno) : base(msg) { } - public MySqlException(SerializationInfo info, + internal MySqlException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/mcs/class/ByteFX.Data/mysqlclient/Field.cs b/mcs/class/ByteFX.Data/mysqlclient/Field.cs index 4e81e6a54c6..4e30648224c 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/Field.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/Field.cs @@ -81,14 +81,14 @@ namespace ByteFX.Data.MySqlClient public int NumericPrecision() { if (colType == MySqlDbType.Decimal) - return ((SqlDecimal)value).Precision; + return colLen; return -1; } public int NumericScale() { if (colType == MySqlDbType.Decimal) - return ((SqlDecimal)value).Scale; + return colDecimals; return -1; } @@ -143,7 +143,7 @@ namespace ByteFX.Data.MySqlClient // read in the data byte[] data = new byte[ len ]; - p.ReadBytes( data, 0, len ); + p.Read( data, 0, len ); // if it is a blob and binary, then GetBytes is the way to go if ( IsBlob() && IsBinary() ) @@ -172,7 +172,7 @@ namespace ByteFX.Data.MySqlClient value = Int16.Parse( sValue ); break; - case MySqlDbType.Long : + case MySqlDbType.Int : case MySqlDbType.Int24: if (IsUnsigned()) value = UInt32.Parse( sValue ); @@ -180,7 +180,7 @@ namespace ByteFX.Data.MySqlClient value = Int32.Parse( sValue ); break; - case MySqlDbType.LongLong: + case MySqlDbType.BigInt: if (IsUnsigned()) value = UInt64.Parse( sValue ); else @@ -264,12 +264,12 @@ namespace ByteFX.Data.MySqlClient case MySqlDbType.Decimal: return "DECIMAL"; case MySqlDbType.Byte: return "TINY"; case MySqlDbType.Short: return "SHORT"; - case MySqlDbType.Long: return "LONG"; + case MySqlDbType.Int: return "INTEGER"; case MySqlDbType.Float: return "FLOAT"; case MySqlDbType.Double: return "DOUBLE"; case MySqlDbType.Null: return "NULL"; case MySqlDbType.Timestamp: return "TIMESTAMP"; - case MySqlDbType.LongLong: return "LONGLONG"; + case MySqlDbType.BigInt: return "BIGINT"; case MySqlDbType.Int24: return "INT24"; case MySqlDbType.Date: return "DATE"; case MySqlDbType.Time: return "TIME"; @@ -297,10 +297,10 @@ namespace ByteFX.Data.MySqlClient case MySqlDbType.Short: return IsUnsigned() ? typeof(System.UInt16) : typeof(System.Int16); - case MySqlDbType.Long: + case MySqlDbType.Int: case MySqlDbType.Int24: return IsUnsigned() ? typeof(System.UInt32) : typeof(System.Int32); - case MySqlDbType.LongLong: return IsUnsigned() ? typeof(System.UInt64) : typeof(System.Int64); + case MySqlDbType.BigInt: return IsUnsigned() ? typeof(System.UInt64) : typeof(System.Int64); case MySqlDbType.Float: return typeof(System.Single); case MySqlDbType.Double: return typeof(System.Double); @@ -343,7 +343,7 @@ namespace ByteFX.Data.MySqlClient else return DbType.Int16; - case MySqlDbType.Long: + case MySqlDbType.Int: if (IsUnsigned()) return DbType.UInt32; else @@ -353,7 +353,7 @@ namespace ByteFX.Data.MySqlClient case MySqlDbType.Double: return DbType.Double; case MySqlDbType.Null: return DbType.Object; - case MySqlDbType.LongLong: + case MySqlDbType.BigInt: if (IsUnsigned()) return DbType.UInt64; else diff --git a/mcs/class/ByteFX.Data/mysqlclient/MySqlHelper.cs b/mcs/class/ByteFX.Data/mysqlclient/MySqlHelper.cs index ad07b89875a..c2b710821a3 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/MySqlHelper.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/MySqlHelper.cs @@ -5,7 +5,7 @@ using ByteFX.Data.MySqlClient; namespace ByteFX.Data.MySqlClient { /// <summary> - /// Summary description for MySqlHelper. + /// Helper class that makes it easier to work with the provider. /// </summary> public sealed class MySqlHelper { @@ -15,6 +15,15 @@ namespace ByteFX.Data.MySqlClient } #region ExecuteNonQuery + + /// <summary> + /// Executes a single command against a MySQL database. The <see cref="MySqlConnection"/> is assumed to be + /// open when the method is called and remains open after the method completes. + /// </summary> + /// <param name="connection"><see cref="MySqlConnection"/> object to use</param> + /// <param name="commandText">SQL command to be executed</param> + /// <param name="commandParameters">Array of <see cref="MySqlParameter"/> objects to use with the command.</param> + /// <returns></returns> public static int ExecuteNonQuery( MySqlConnection connection, string commandText, params MySqlParameter[] commandParameters ) { //create a command and prepare it for execution @@ -33,6 +42,14 @@ namespace ByteFX.Data.MySqlClient return result; } + /// <summary> + /// Executes a single command against a MySQL database. A new <see cref="MySqlConnection"/> is created + /// using the <see cref="MySqlConnection.ConnectionString"/> given. + /// </summary> + /// <param name="connectionString"><see cref="MySqlConnection.ConnectionString"/> to use</param> + /// <param name="commandText">SQL command to be executed</param> + /// <param name="parms">Array of <see cref="MySqlParameter"/> objects to use with the command.</param> + /// <returns></returns> public static int ExecuteNonQuery( string connectionString, string commandText, params MySqlParameter[] parms ) { //create & open a SqlConnection, and dispose of it after we are done. @@ -47,6 +64,15 @@ namespace ByteFX.Data.MySqlClient #endregion #region ExecuteDataSet + + /// <summary> + /// Executes a single SQL command and returns the first row of the resultset. A new MySqlConnection object + /// is created, opened, and closed during this method. + /// </summary> + /// <param name="connectionString">Settings to be used for the connection</param> + /// <param name="commandText">Command to execute</param> + /// <param name="parms">Parameters to use for the command</param> + /// <returns>DataRow containing the first row of the resultset</returns> public static DataRow ExecuteDatarow( string connectionString, string commandText, params MySqlParameter[] parms ) { DataSet ds = ExecuteDataset( connectionString, commandText, parms ); @@ -56,12 +82,27 @@ namespace ByteFX.Data.MySqlClient return ds.Tables[0].Rows[0]; } + /// <summary> + /// Executes a single SQL command and returns the resultset in a <see cref="DataSet"/>. + /// A new MySqlConnection object is created, opened, and closed during this method. + /// </summary> + /// <param name="connectionString">Settings to be used for the connection</param> + /// <param name="commandText">Command to execute</param> + /// <returns><see cref="DataSet"/> containing the resultset</returns> public static DataSet ExecuteDataset(string connectionString, string commandText) { //pass through the call providing null for the set of SqlParameters return ExecuteDataset(connectionString, commandText, (MySqlParameter[])null); } + /// <summary> + /// Executes a single SQL command and returns the resultset in a <see cref="DataSet"/>. + /// A new MySqlConnection object is created, opened, and closed during this method. + /// </summary> + /// <param name="connectionString">Settings to be used for the connection</param> + /// <param name="commandText">Command to execute</param> + /// <param name="commandParameters">Parameters to use for the command</param> + /// <returns><see cref="DataSet"/> containing the resultset</returns> public static DataSet ExecuteDataset(string connectionString, string commandText, params MySqlParameter[] commandParameters) { //create & open a SqlConnection, and dispose of it after we are done. @@ -74,13 +115,29 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Executes a single SQL command and returns the resultset in a <see cref="DataSet"/>. + /// The state of the <see cref="MySqlConnection"/> object remains unchanged after execution + /// of this method. + /// </summary> + /// <param name="connection"><see cref="MySqlConnection"/> object to use</param> + /// <param name="commandText">Command to execute</param> + /// <returns><see cref="DataSet"/> containing the resultset</returns> public static DataSet ExecuteDataset(MySqlConnection connection, string commandText) { //pass through the call providing null for the set of SqlParameters return ExecuteDataset(connection, commandText, (MySqlParameter[])null); } - + /// <summary> + /// Executes a single SQL command and returns the resultset in a <see cref="DataSet"/>. + /// The state of the <see cref="MySqlConnection"/> object remains unchanged after execution + /// of this method. + /// </summary> + /// <param name="connection"><see cref="MySqlConnection"/> object to use</param> + /// <param name="commandText">Command to execute</param> + /// <param name="commandParameters">Parameters to use for the command</param> + /// <returns><see cref="DataSet"/> containing the resultset</returns> public static DataSet ExecuteDataset(MySqlConnection connection, string commandText, params MySqlParameter[] commandParameters) { //create a command and prepare it for execution @@ -107,6 +164,13 @@ namespace ByteFX.Data.MySqlClient return ds; } + /// <summary> + /// Updates the given table with data from the given <see cref="DataSet"/> + /// </summary> + /// <param name="connectionString">Settings to use for the update</param> + /// <param name="commandText">Command text to use for the update</param> + /// <param name="ds"><see cref="DataSet"/> containing the new data to use in the update</param> + /// <param name="tablename">Tablename in the dataset to update</param> public static void UpdateDataSet( string connectionString, string commandText, DataSet ds, string tablename ) { MySqlConnection cn = new MySqlConnection( connectionString ); @@ -120,6 +184,16 @@ namespace ByteFX.Data.MySqlClient #endregion #region ExecuteDataReader + + /// <summary> + /// Executes a single command against a MySQL database, possibly inside an existing transaction. + /// </summary> + /// <param name="connection"><see cref="MySqlConnection"/> object to use for the command</param> + /// <param name="transaction"><see cref="MySqlTransaction"/> object to use for the command</param> + /// <param name="commandText">Command text to use</param> + /// <param name="commandParameters">Array of <see cref="MySqlParameter"/> objects to use with the command</param> + /// <param name="ExternalConn">True if the connection should be preserved, false if not</param> + /// <returns><see cref="MySqlDataReader"/> object ready to read the results of the command</returns> private static MySqlDataReader ExecuteReader(MySqlConnection connection, MySqlTransaction transaction, string commandText, MySqlParameter[] commandParameters, bool ExternalConn ) { //create a command and prepare it for execution @@ -152,12 +226,25 @@ namespace ByteFX.Data.MySqlClient return dr; } + /// <summary> + /// Executes a single command against a MySQL database. + /// </summary> + /// <param name="connectionString">Settings to use for this command</param> + /// <param name="commandText">Command text to use</param> + /// <returns><see cref="MySqlDataReader"/> object ready to read the results of the command</returns> public static MySqlDataReader ExecuteReader(string connectionString, string commandText) { //pass through the call providing null for the set of SqlParameters return ExecuteReader(connectionString, commandText, (MySqlParameter[])null); } + /// <summary> + /// Executes a single command against a MySQL database. + /// </summary> + /// <param name="connectionString">Settings to use for this command</param> + /// <param name="commandText">Command text to use</param> + /// <param name="commandParameters">Array of <see cref="MySqlParameter"/> objects to use with the command</param> + /// <returns><see cref="MySqlDataReader"/> object ready to read the results of the command</returns> public static MySqlDataReader ExecuteReader(string connectionString, string commandText, params MySqlParameter[] commandParameters) { //create & open a SqlConnection @@ -179,12 +266,26 @@ namespace ByteFX.Data.MySqlClient #endregion #region ExecuteScalar + + /// <summary> + /// Execute a single command against a MySQL database. + /// </summary> + /// <param name="connectionString">Settings to use for the update</param> + /// <param name="commandText">Command text to use for the update</param> + /// <returns>The first column of the first row in the result set, or a null reference if the result set is empty.</returns> public static object ExecuteScalar(string connectionString, string commandText) { //pass through the call providing null for the set of MySqlParameters return ExecuteScalar(connectionString, commandText, (MySqlParameter[])null); } + /// <summary> + /// Execute a single command against a MySQL database. + /// </summary> + /// <param name="connectionString">Settings to use for the command</param> + /// <param name="commandText">Command text to use for the command</param> + /// <param name="commandParameters">Parameters to use for the command</param> + /// <returns>The first column of the first row in the result set, or a null reference if the result set is empty.</returns> public static object ExecuteScalar(string connectionString, string commandText, params MySqlParameter[] commandParameters) { //create & open a SqlConnection, and dispose of it after we are done. @@ -197,12 +298,25 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Execute a single command against a MySQL database. + /// </summary> + /// <param name="connection"><see cref="MySqlConnection"/> object to use</param> + /// <param name="commandText">Command text to use for the command</param> + /// <returns>The first column of the first row in the result set, or a null reference if the result set is empty.</returns> public static object ExecuteScalar(MySqlConnection connection, string commandText) { //pass through the call providing null for the set of MySqlParameters return ExecuteScalar(connection, commandText, (MySqlParameter[])null); } + /// <summary> + /// Execute a single command against a MySQL database. + /// </summary> + /// <param name="connection"><see cref="MySqlConnection"/> object to use</param> + /// <param name="commandText">Command text to use for the command</param> + /// <param name="commandParameters">Parameters to use for the command</param> + /// <returns>The first column of the first row in the result set, or a null reference if the result set is empty.</returns> public static object ExecuteScalar(MySqlConnection connection, string commandText, params MySqlParameter[] commandParameters) { //create a command and prepare it for execution diff --git a/mcs/class/ByteFX.Data/mysqlclient/MySqlPool.cs b/mcs/class/ByteFX.Data/mysqlclient/MySqlPool.cs index 718fd53592e..bc4828c5e19 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/MySqlPool.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/MySqlPool.cs @@ -9,26 +9,43 @@ namespace ByteFX.Data.MySqlClient /// </summary> internal sealed class MySqlPool { - private ArrayList inUsePool; - private ArrayList idlePool; - private int minSize; - private int maxSize; + private ArrayList inUsePool; + private ArrayList idlePool; + private MySqlConnectionString settings; + private int minSize; + private int maxSize; - public MySqlPool(int minSize, int maxSize) + public MySqlPool(MySqlConnectionString settings) + { + minSize = settings.MinPoolSize; + maxSize = settings.MaxPoolSize; + this.settings = settings; + inUsePool =new ArrayList(); + idlePool = new ArrayList( settings.MinPoolSize ); + + // prepopulate the idle pool to minSize + for (int i=0; i < minSize; i++) + CreateNewPooledConnection(); + } + + private void CheckOutConnection(MySqlInternalConnection conn) { - this.minSize = minSize; - this.maxSize = maxSize; - inUsePool =new ArrayList(minSize); - idlePool = new ArrayList(minSize); } private MySqlInternalConnection GetPooledConnection() { + MySqlInternalConnection conn = null; + + // if there are no idle connections and the in use pool is full + // then return null to indicate that we cannot provide a connection + // at this time. + if (idlePool.Count == 0 && inUsePool.Count == maxSize) return null; + lock (idlePool.SyncRoot) { for (int i=idlePool.Count-1; i >=0; i--) { - MySqlInternalConnection conn = (idlePool[i] as MySqlInternalConnection); + conn = (idlePool[i] as MySqlInternalConnection); if (conn.IsAlive()) { lock (inUsePool) @@ -45,20 +62,44 @@ namespace ByteFX.Data.MySqlClient } } } - return null; + + // if we couldn't get a pooled connection and there is still room + // make a new one + if (conn == null && (idlePool.Count+inUsePool.Count) < maxSize) + { + conn = CreateNewPooledConnection(); + if (conn == null) return null; + + lock (idlePool.SyncRoot) + lock (inUsePool.SyncRoot) + { + idlePool.Remove( conn ); + inUsePool.Add( conn ); + } + } + + return conn; } - private MySqlInternalConnection CreateNewPooledConnection( MySqlConnectionString settings ) + private MySqlInternalConnection CreateNewPooledConnection() { - MySqlInternalConnection conn = new MySqlInternalConnection( settings ); - conn.Open(); - return conn; + lock(idlePool.SyncRoot) + lock (inUsePool.SyncRoot) + { + // first we check if we are allowed to create another + if ((inUsePool.Count + idlePool.Count) == maxSize) return null; + + MySqlInternalConnection conn = new MySqlInternalConnection( settings ); + conn.Open(); + idlePool.Add( conn ); + return conn; + } } public void ReleaseConnection( MySqlInternalConnection connection ) { - lock (inUsePool.SyncRoot) - lock (idlePool.SyncRoot) + lock (idlePool.SyncRoot) + lock (inUsePool.SyncRoot) { inUsePool.Remove( connection ); if (connection.Settings.ConnectionLifetime != 0 && connection.IsTooOld()) @@ -68,20 +109,16 @@ namespace ByteFX.Data.MySqlClient } } - public MySqlInternalConnection GetConnection(MySqlConnectionString settings) + public MySqlInternalConnection GetConnection() { - MySqlInternalConnection conn; + MySqlInternalConnection conn = null; - DateTime start = DateTime.Now; - TimeSpan ts; - do - { - conn = GetPooledConnection(); - if (conn == null) - conn = CreateNewPooledConnection( settings ); - ts = DateTime.Now.Subtract( start ); - } while (conn == null && ts.Seconds < settings.ConnectTimeout ); + int start = Environment.TickCount; + int ticks = settings.ConnectTimeout * 1000; + // wait timeOut seconds at most to get a connection + while (conn == null && (Environment.TickCount - start) < ticks) + conn = GetPooledConnection(); // if pool size is at maximum, then we must have reached our timeout so we simply // throw our exception diff --git a/mcs/class/ByteFX.Data/mysqlclient/MySqlPoolManager.cs b/mcs/class/ByteFX.Data/mysqlclient/MySqlPoolManager.cs index acc6eba4d35..d1b418476ef 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/MySqlPoolManager.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/MySqlPoolManager.cs @@ -36,7 +36,7 @@ namespace ByteFX.Data.MySqlClient MySqlPool pool; if (!pools.Contains( text )) { - pool = new MySqlPool( settings.MinPoolSize, settings.MaxPoolSize ); + pool = new MySqlPool( settings ); pools.Add( text, pool ); } else @@ -44,7 +44,7 @@ namespace ByteFX.Data.MySqlClient pool = (pools[text] as MySqlPool); } - return pool.GetConnection( settings ); + return pool.GetConnection(); } } diff --git a/mcs/class/ByteFX.Data/mysqlclient/MySqlStream.cs b/mcs/class/ByteFX.Data/mysqlclient/MySqlStream.cs index a7d33ec9230..faa027765a6 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/MySqlStream.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/MySqlStream.cs @@ -20,124 +20,96 @@ using System.IO; using System.Net; using System.Net.Sockets; using ByteFX.Data.Common; +using System.Threading; namespace ByteFX.Data.MySqlClient { /// <summary> /// Summary description for API. /// </summary> - internal class MySqlStream : Stream + internal class MySqlStream : MultiHostStream { - Stream stream; - Socket socket; - int timeOut; + public MySqlStream( string hostList, int port, int readTimeOut, int connectTimeOut ) : + base( hostList, port, readTimeOut, connectTimeOut ) - public MySqlStream( string host, int port, int timeout ) { - if (port == -1) - Create( host ); - else - Create( host, port ); - timeOut = timeout; } - private void Create( string host, int port ) + protected override void Error(string msg) { - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - IPHostEntry he = Dns.GetHostByName( host ); - IPEndPoint serverAddr = new IPEndPoint(he.AddressList[0], port); - - socket.Connect(serverAddr); - stream = new NetworkStream(socket, true); + throw new MySqlException( msg, baseException ); } - private void Create( string host ) + protected override void TimeOut(MultiHostStreamErrorType error) { - string pipeName; - - if (host.ToLower().Equals("localhost")) - pipeName = @"\\.\pipe\MySql"; - else - pipeName = String.Format(@"\\{0}\pipe\MySql", host); - - stream = new ByteFX.Data.Common.NamedPipeStream(pipeName, FileAccess.ReadWrite); - } - - public bool DataAvailable - { - get + switch (error) { - if (stream is NetworkStream) - return ((NetworkStream)stream).DataAvailable; - else return (stream as NamedPipeStream).DataAvailable; + case MultiHostStreamErrorType.Connecting: + throw new MySqlException("Timed out creating a new MySqlConnection"); + case MultiHostStreamErrorType.Reading: + throw new MySqlException("Timed out reading from MySql"); } - } - - public override bool CanRead - { - get { return stream.CanRead; } - } - - public override bool CanWrite - { - get { return stream.CanWrite; } } - public override bool CanSeek + protected override bool CreateStream( IPAddress ip, string hostname, int port ) { - get { return stream.CanSeek; } + if (port == -1) + return CreatePipeStream(ip, hostname); + else + return CreateSocketStream(ip, port); } - public override long Length + private bool CreatePipeStream( IPAddress ip, string hostname ) { - get { return stream.Length; } - } + string pipeName; - public override long Position - { - get { return stream.Position; } - set { stream.Position = value; } - } + if (hostname.ToLower().Equals("localhost")) + pipeName = @"\\.\pipe\MySql"; + else + pipeName = String.Format(@"\\{0}\pipe\MySql", ip.ToString()); - public override void Flush() - { - stream.Flush(); + try + { + stream = new NamedPipeStream(pipeName, FileAccess.ReadWrite); + return true; + } + catch (Exception ex) + { + baseException = ex; + return false; + } } - public override int ReadByte() + private bool CreateSocketStream( IPAddress ip, int port ) { - long start = Environment.TickCount; - long timeout_ticks = timeOut * TimeSpan.TicksPerSecond; + Socket socket = new Socket(AddressFamily.InterNetwork, + SocketType.Stream, ProtocolType.Tcp); - while (((Environment.TickCount - start) < timeout_ticks)) + try { - if (DataAvailable) - { - int b = stream.ReadByte(); - return b; - } + // + // Lets try to connect + IPEndPoint endPoint = new IPEndPoint( ip, port); + socket.Connect(endPoint); + socket.SetSocketOption( SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1 ); + stream = new NetworkStream( socket, true ); + return true; + } + catch (Exception ex) + { + baseException = ex; + return false; } - throw new Exception("Timeout waiting for response from server"); } - public override int Read(byte[] buffer, int offset, int count) + protected override bool DataAvailable { - long start = Environment.TickCount; - int numToRead = count; - long timeout_ticks = timeOut * TimeSpan.TicksPerSecond; - - while (numToRead > 0 && ((Environment.TickCount - start) < timeout_ticks)) + get { - if (DataAvailable) - { - int bytes_read = stream.Read(buffer, offset, numToRead); - offset += bytes_read; - numToRead -= bytes_read; - } + if (stream is NetworkStream) + return ((NetworkStream)stream).DataAvailable; + else return (stream as NamedPipeStream).DataAvailable; } - if (numToRead > 0) - throw new Exception("Timeout waiting for response from server"); - return count; } public int ReadInt24() @@ -152,20 +124,6 @@ namespace ByteFX.Data.MySqlClient stream.Close(); } - public override void SetLength(long length) - { - stream.SetLength( length ); - } - - public override void Write(byte[] buffer, int offset, int count) - { - stream.Write( buffer, offset, count ); - } - - public override long Seek( long offset, SeekOrigin origin ) - { - return stream.Seek( offset, origin ); - } } } diff --git a/mcs/class/ByteFX.Data/mysqlclient/MysqlDefs.cs b/mcs/class/ByteFX.Data/mysqlclient/MysqlDefs.cs index eed7d80ad0d..570e48eeb8d 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/MysqlDefs.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/MysqlDefs.cs @@ -69,30 +69,62 @@ namespace ByteFX.Data.MySqlClient CHANGE_USER = 17, } + /// <summary> + /// Specifies MySQL specific data type of a field, property, for use in a <see cref="MySqlParameter"/>. + /// </summary> public enum MySqlDbType { - Decimal = 0, - Byte = 1, + /// <summary> <see cref="Decimal"/><para>A fixed precision and scale numeric value between -1038 -1 and 10 38 -1.</para></summary> + Decimal = 0, + /// <summary> <see cref="Byte"/><para>The signed range is -128 to 127. The unsigned range is 0 to 255.</para></summary> + Byte = 1, + /// <summary><see cref="Int16"/><para>A 16-bit signed or unsigned integer. The signed range is -32768 to 32767. The unsigned range is 0 to 65535</para></summary> Short = 2, + /// <summary><see cref="Int32"/><para>A 32-bit signed or unsigned integer</para></summary> + Int = 3, + /// <summary><b>Obsolete</b> Please use Int for 32 bit values</summary> + [Obsolete("Long is no longer the correct way of specifying a 32 bit value. Use Int")] Long = 3, + /// <summary><see cref="Single"/><para>A small (single-precision) floating-point number. Allowable values are -3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to 3.402823466E+38.</para></summary> Float = 4, + /// <summary><see cref="Double"/><para>A normal-size (double-precision) floating-point number. Allowable values are -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and 2.2250738585072014E-308 to 1.7976931348623157E+308.</para></summary> Double = 5, + /// <summary>Specifies a null value</summary> Null = 6, + /// <summary>A timestamp. The range is '1970-01-01 00:00:00' to sometime in the year 2037</summary> Timestamp = 7, + /// <summary><see cref="Int64"/><para>A 64-bit signed or unsigned integer.</para></summary> + BigInt = 8, + /// <summary><b>Obsolete</b> Please use BigInt for 64 bit values</summary> + [Obsolete("LongLong is no longer the correct way of specifying a 64 bit value. Use BigInt")] LongLong = 8, + /// <summary>Specifies a 24 (3 byte) signed or unsigned value.</summary> Int24 = 9, + ///<summary>Date The supported range is '1000-01-01' to '9999-12-31'.</summary> Date = 10, + /// <summary> Time <para>The range is '-838:59:59' to '838:59:59'.</para></summary> Time = 11, - Datetime = 12, + ///<summary>DateTime The supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'.</summary> + Datetime = 12, + /// <summary>A year in 2- or 4-digit format (default is 4-digit). The allowable values are 1901 to 2155, 0000 in the 4-digit year format, and 1970-2069 if you use the 2-digit format (70-69)</summary> Year = 13, + /// <summary><b>Obsolete</b> Use Datetime or Date type</summary> Newdate = 14, + /// <summary>An enumeration. A string object that can have only one value, chosen from the list of values 'value1', 'value2', ..., NULL or the special "" error value. An ENUM can have a maximum of 65535 distinct values</summary> Enum = 247, + /// <summary>A set. A string object that can have zero or more values, each of which must be chosen from the list of values 'value1', 'value2', ... A SET can have a maximum of 64 members.</summary> Set = 248, + /// <summary>A BLOB or TEXT column with a maximum length of 255 (2^8 - 1) characters</summary> TinyBlob = 249, + /// <summary>A BLOB or TEXT column with a maximum length of 16777215 (2^24 - 1) characters</summary> MediumBlob = 250, + /// <summary>A BLOB or TEXT column with a maximum length of 4294967295 or 4G (2^32 - 1) characters</summary> LongBlob = 251, + /// <summary>A BLOB or TEXT column with a maximum length of 65535 (2^16 - 1) characters</summary> Blob = 252, + /// <summary>A variable-length string containing 0 to 255 characters</summary> VarChar = 253, + /// <summary><b>Obsolete</b> Use VarChar type</summary> String = 254 }; diff --git a/mcs/class/ByteFX.Data/mysqlclient/Packet.cs b/mcs/class/ByteFX.Data/mysqlclient/Packet.cs index 25634989768..ca4685e93df 100644 --- a/mcs/class/ByteFX.Data/mysqlclient/Packet.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/Packet.cs @@ -4,7 +4,7 @@ using System.Text; namespace ByteFX.Data.MySqlClient { - internal enum PacketType +/* internal enum PacketType { None, UpdateOrOk, @@ -14,32 +14,28 @@ namespace ByteFX.Data.MySqlClient Error, LoadDataLocal, Other - } + }*/ /// <summary> /// Summary description for Packet. /// </summary> - internal class Packet + internal class Packet : MemoryStream { - MemoryStream data; - PacketType type = PacketType.None; Encoding encoding; + private static int HEADER_LEN = 7; - public Packet() + public Packet() : base(256+HEADER_LEN) { - data = new MemoryStream(); + Position = HEADER_LEN; } - public Packet(int len) + public Packet(int len) : base(len+HEADER_LEN) { - data = new MemoryStream(len); + Position = HEADER_LEN; } - public Packet(byte[] bytes) + public Packet(byte[] bytes) : base(bytes, 0, bytes.Length, true, true) { - data = new MemoryStream( bytes.Length ); - data.Write( bytes, 0, bytes.Length ); - data.Position = 0; } public Encoding Encoding @@ -48,73 +44,30 @@ namespace ByteFX.Data.MySqlClient get { return encoding; } } - public int Length - { - get { return (int)data.Length; } - } - - public PacketType Type - { - get { if (type == PacketType.None) ParseType(); return type; } - set { type = value; } - } - - public long Position - { - get { return data.Position; } - set { data.Position = value; } - } - - public void AppendPacket( Packet newPacket ) - { - data.Position = data.Length; - byte[] bytes = newPacket.GetBytes(); - data.Write( bytes, 0, bytes.Length ); - } - - private PacketType ParseType() - { - byte b = ReadByte(); - - // a 1 byte packet with byte 0xfe means last packet - if ( data.Length == 1 && b == 0xfe) - type = PacketType.Last; - - // a first byte of 0xff means the packet is an error message - else if ( b == 0xff ) - type = PacketType.Error; - - // the first byte == 0 means an update packet or column count - else if ( b == 0 ) - type = PacketType.UpdateOrOk; - else - type = PacketType.Other; - return type; - } - - public byte[] GetBytes() - { - return data.ToArray(); - } - - public void WriteByte( byte b ) + public byte this[int index] { - data.WriteByte( b ); + get { return GetBuffer()[index]; } } - public byte ReadByte() + public new int Length { - return (byte)data.ReadByte(); + get { return (int)base.Length; } } - public void ReadBytes( byte[] buffer, int offset, int len ) + public bool IsLastPacket() { - data.Read( buffer, offset, len ); + if (Length == 1 && this[0] == 0xfe) return true; + return false; } - public void WriteBytes( byte[] bytes, int offset, int len ) + public byte[] GetBytes( byte packetSeq ) { - data.Write( bytes, offset, len ); + long oldPos = Position; + Position = 3; + WriteInteger( Length-HEADER_LEN, 3 ); + WriteByte( packetSeq ); + Position = oldPos; + return GetBuffer(); } public int ReadNBytes() @@ -129,7 +82,7 @@ namespace ByteFX.Data.MySqlClient int len = ReadLenInteger(); byte[] buffer = new Byte[len]; - ReadBytes(buffer, 0, len); + Read(buffer, 0, len); return encoding.GetString( buffer, 0, len); } @@ -148,7 +101,7 @@ namespace ByteFX.Data.MySqlClient for (int x=0; x < numbytes; x++) { - data.WriteByte( (byte)(val&0xff) ); + WriteByte( (byte)(val&0xff) ); val >>= 8; } } @@ -164,7 +117,7 @@ namespace ByteFX.Data.MySqlClient int raise = 1; for (int x=0; x < numbytes; x++) { - int b = data.ReadByte(); + int b = ReadByte(); val += (b*raise); raise *= 256; } @@ -189,9 +142,9 @@ namespace ByteFX.Data.MySqlClient } } - public bool CanRead + public bool HasMoreData { - get { return data.Position < data.Length; } + get { return Position < Length; } } #region String Functions @@ -199,9 +152,9 @@ namespace ByteFX.Data.MySqlClient { System.Text.StringBuilder sb = new System.Text.StringBuilder(); - while ( CanRead ) + while ( HasMoreData ) { - byte b = ReadByte(); + byte b = (byte)ReadByte(); if (b == 0) break; sb.Append( Convert.ToChar( b )); } @@ -212,13 +165,13 @@ namespace ByteFX.Data.MySqlClient public void WriteString(string v, Encoding encoding) { WriteStringNoNull(v, encoding); - data.WriteByte(0); + WriteByte(0); } public void WriteStringNoNull(string v, Encoding encoding) { byte[] bytes = encoding.GetBytes(v); - data.Write(bytes, 0, bytes.Length); + Write(bytes, 0, bytes.Length); } #endregion diff --git a/mcs/class/ByteFX.Data/mysqlclient/command.cs b/mcs/class/ByteFX.Data/mysqlclient/command.cs index 0a1224afea1..1dd3a20e5f2 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/command.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/command.cs @@ -22,10 +22,13 @@ using System.Collections; namespace ByteFX.Data.MySqlClient { + /// <summary> + /// Represents a SQL statement to execute against a MySQL database. This class cannot be inherited. + /// </summary> + /// <include file='docs/MySqlCommand.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> #if WINDOWS [System.Drawing.ToolboxBitmap( typeof(MySqlCommand), "Designers.command.bmp")] #endif - [System.ComponentModel.DesignerCategory("Code")] public sealed class MySqlCommand : Component, IDbCommand, ICloneable { @@ -44,31 +47,43 @@ namespace ByteFX.Data.MySqlClient { } - // Implement other constructors here. + /// <summary> + /// Overloaded. Initializes a new instance of the MySqlCommand class. + /// </summary> public MySqlCommand(string cmdText) { this.cmdText = cmdText; } + /// <summary> + /// Overloaded. Initializes a new instance of the MySqlCommand class. + /// </summary> public MySqlCommand(System.ComponentModel.IContainer container) { - /// <summary> - /// Required for Windows.Forms Class Composition Designer support - /// </summary> + // Required for Windows.Forms Class Composition Designer support container.Add(this); } + /// <summary> + /// Overloaded. Initializes a new instance of the MySqlCommand class. + /// </summary> public MySqlCommand(string cmdText, MySqlConnection connection) { this.cmdText = cmdText; this.connection = connection; } + /// <summary> + /// Disposes of this instance of MySqlCommand + /// </summary> public new void Dispose() { base.Dispose(); } + /// <summary> + /// Overloaded. Initializes a new instance of the MySqlCommand class. + /// </summary> public MySqlCommand(string cmdText, MySqlConnection connection, MySqlTransaction txn) { this.cmdText = cmdText; @@ -92,7 +107,7 @@ namespace ByteFX.Data.MySqlClient set { cmdText = value; } } - public int UpdateCount + internal int UpdateCount { get { return updateCount; } } @@ -207,6 +222,10 @@ namespace ByteFX.Data.MySqlClient #endregion #region Methods + /// <summary> + /// Attempts to cancel the execution of a MySqlCommand. This operation is not supported. + /// </summary> + /// <exception cref="NotSupportedException">This operation is not supported.</exception> public void Cancel() { throw new NotSupportedException(); @@ -229,7 +248,7 @@ namespace ByteFX.Data.MySqlClient private ArrayList SplitSql(string sql) { ArrayList commands = new ArrayList(); - System.IO.MemoryStream ms = new System.IO.MemoryStream(); + System.IO.MemoryStream ms = new System.IO.MemoryStream(sql.Length); // first we tack on a semi-colon, if not already there, to make our // sql processing code easier. Then we ask our encoder to give us @@ -292,7 +311,7 @@ namespace ByteFX.Data.MySqlClient if (cmdByte != ' ') { goodcmd = true; break; } if (goodcmd) - commands.Add( ms.ToArray() ); + commands.Add( byteArray ); ms.SetLength(0); } else if (parm_start == -1) @@ -306,6 +325,21 @@ namespace ByteFX.Data.MySqlClient return commands; } + private void ReadOffResultSet() + { + Driver driver = connection.InternalConnection.Driver; + + // first read off the schema + Packet packet = driver.ReadPacket(); + while (! packet.IsLastPacket()) + packet = driver.ReadPacket(); + + // now read off the data + packet = driver.ReadPacket(); + while (! packet.IsLastPacket()) + packet = driver.ReadPacket(); + } + /// <summary> /// Internal function to execute the next command in an array of commands /// </summary> @@ -318,16 +352,20 @@ namespace ByteFX.Data.MySqlClient byte[] sql = (byte[])arraySql[0]; arraySql.RemoveAt(0); - string s = connection.Encoding.GetString(sql); - Packet packet = driver.SendSql( s ); - if (packet.Type == PacketType.UpdateOrOk) + Packet packet = driver.SendSql( sql ); + byte b = (byte)packet.ReadByte(); + if (b == 0) + { + if (updateCount == -1) updateCount = 0; updateCount += (int)packet.ReadLenInteger(); - else if (packet.Type == PacketType.ResultSchema && stopAtResultSet) + } + else if (stopAtResultSet) + { + packet.Position--; return packet; - else do - { - packet = driver.ReadPacket(); - } while (packet.Type != PacketType.Last); + } + else + ReadOffResultSet(); } return null; } @@ -347,7 +385,7 @@ namespace ByteFX.Data.MySqlClient throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first."); // execute any commands left in the queue from before. - ExecuteBatch(false); + //ExecuteBatch(false); arraySql = SplitSql( cmdText ); updateCount = 0; @@ -376,14 +414,13 @@ namespace ByteFX.Data.MySqlClient return ExecuteReader(CommandBehavior.Default); } + + /// <summary> + /// Overloaded. Sends the CommandText to the Connection and builds a MySqlDataReader. + /// </summary> + /// <returns></returns> public MySqlDataReader ExecuteReader(CommandBehavior behavior) { - /* - * ExecuteReader should retrieve results from the data source - * and return a DataReader that allows the user to process - * the results. - */ - // There must be a valid and open connection. if (connection == null || connection.State != ConnectionState.Open) throw new InvalidOperationException("Connection must be valid and open"); @@ -415,27 +452,15 @@ namespace ByteFX.Data.MySqlClient sql = String.Format("SET SQL_SELECT_LIMIT=1;{0};SET sql_select_limit=-1;", cmdText); } - // execute any commands left in the queue from before. - ExecuteBatch(false); - arraySql = SplitSql( sql ); + updateCount = -1; MySqlDataReader reader = new MySqlDataReader(this, behavior); - try - { - if (reader.NextResult()) - { - connection.Reader = reader; - return reader; - } - return null; - } - catch (Exception e) - { - System.Diagnostics.Trace.WriteLine("Exception in ExecuteReader: " + e.Message); - throw e; - } + // move to the first resultset + reader.NextResult(); + connection.Reader = reader; + return reader; } /// <summary> @@ -453,11 +478,9 @@ namespace ByteFX.Data.MySqlClient if (connection.Reader != null) throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first."); - // execute any commands left in the queue from before. - ExecuteBatch(false); - arraySql = SplitSql( cmdText ); + updateCount = -1; MySqlDataReader reader = new MySqlDataReader(this, 0); reader.NextResult(); object val = null; @@ -477,12 +500,17 @@ namespace ByteFX.Data.MySqlClient #endregion #region ICloneable + /// <summary> + /// Creates a clone of this MySqlCommand object. CommandText, Connection, and Transaction properties + /// are included as well as the entire parameter list. + /// </summary> + /// <returns>The cloned MySqlCommand object</returns> public object Clone() { MySqlCommand clone = new MySqlCommand(cmdText, connection, curTransaction); foreach (MySqlParameter p in parameters) { - clone.Parameters.Add(p.Clone()); + clone.Parameters.Add((p as ICloneable).Clone()); } return clone; } diff --git a/mcs/class/ByteFX.Data/mysqlclient/dataadapter.cs b/mcs/class/ByteFX.Data/mysqlclient/dataadapter.cs index e6e288177c3..80637834834 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/dataadapter.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/dataadapter.cs @@ -21,6 +21,10 @@ using System.ComponentModel; namespace ByteFX.Data.MySqlClient { + /// <summary> + /// Represents a set of data commands and a database connection that are used to fill a dataset and update a MySQL database. This class cannot be inherited. + /// </summary> + /// <include file='docs/MySqlDataAdapter.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> [System.Drawing.ToolboxBitmap( typeof(MySqlDataAdapter), "Designers.dataadapter.bmp")] [System.ComponentModel.DesignerCategory("Code")] public sealed class MySqlDataAdapter : DbDataAdapter, IDbDataAdapter @@ -40,41 +44,63 @@ namespace ByteFX.Data.MySqlClient static private readonly object EventRowUpdating = new object(); + /// <summary> + /// Initializes a new instance of the MySqlDataAdapter class. + /// </summary> public MySqlDataAdapter() { } + /// <summary> + /// Initializes a new instance of the MySqlDataAdapter class with the specified MySqlCommand as the SelectCommand property. + /// </summary> + /// <param name="selectCommand"></param> public MySqlDataAdapter( MySqlCommand selectCommand ) { SelectCommand = selectCommand; } - public MySqlDataAdapter( string selectCommandText, string selectConnString) + /// <summary> + /// Initializes a new instance of the MySqlDataAdapter class with a SelectCommand and a MySqlConnection object. + /// </summary> + /// <param name="selectCommandText"></param> + /// <param name="conn"></param> + public MySqlDataAdapter( string selectCommandText, MySqlConnection conn) { - SelectCommand = new MySqlCommand( selectCommandText, - new MySqlConnection(selectConnString) ); + SelectCommand = new MySqlCommand( selectCommandText, conn ); } - public MySqlDataAdapter( string selectCommandText, MySqlConnection conn) + /// <summary> + /// Initializes a new instance of the MySqlDataAdapter class with a SelectCommand and a connection string. + /// </summary> + /// <param name="selectCommandText"></param> + /// <param name="selectConnString"></param> + public MySqlDataAdapter( string selectCommandText, string selectConnString) { - SelectCommand = new MySqlCommand( selectCommandText, conn ); + SelectCommand = new MySqlCommand( selectCommandText, + new MySqlConnection(selectConnString) ); } #region Properties - [DataSysDescription("Used during Fill/FillSchema")] - [Category("Fill")] - public MySqlCommand SelectCommand + /// <summary> + /// Gets or sets a SQL statement to delete records from the data set. + /// </summary> + [DataSysDescription("Used during Update for deleted rows in Dataset.")] + public MySqlCommand DeleteCommand { - get { return m_selectCommand; } - set { m_selectCommand = value; } + get { return m_deleteCommand; } + set { m_deleteCommand = value; } } - IDbCommand IDbDataAdapter.SelectCommand + IDbCommand IDbDataAdapter.DeleteCommand { - get { return m_selectCommand; } - set { m_selectCommand = (MySqlCommand)value; } + get { return m_deleteCommand; } + set { m_deleteCommand = (MySqlCommand)value; } } + /// <summary> + /// Gets or sets a SQL statement to insert new records into the data source. + /// </summary> [DataSysDescription("Used during Update for new rows in Dataset.")] public MySqlCommand InsertCommand { @@ -88,6 +114,26 @@ namespace ByteFX.Data.MySqlClient set { m_insertCommand = (MySqlCommand)value; } } + /// <summary> + /// Gets or sets a SQL statement used to select records in the data source. + /// </summary> + [DataSysDescription("Used during Fill/FillSchema")] + [Category("Fill")] + public MySqlCommand SelectCommand + { + get { return m_selectCommand; } + set { m_selectCommand = value; } + } + + IDbCommand IDbDataAdapter.SelectCommand + { + get { return m_selectCommand; } + set { m_selectCommand = (MySqlCommand)value; } + } + + /// <summary> + /// Gets or sets a SQL statement used to update records in the data source. + /// </summary> [DataSysDescription("Used during Update for modified rows in Dataset.")] public MySqlCommand UpdateCommand { @@ -101,33 +147,41 @@ namespace ByteFX.Data.MySqlClient set { m_updateCommand = (MySqlCommand)value; } } - [DataSysDescription("Used during Update for deleted rows in Dataset.")] - public MySqlCommand DeleteCommand - { - get { return m_deleteCommand; } - set { m_deleteCommand = value; } - } - - IDbCommand IDbDataAdapter.DeleteCommand - { - get { return m_deleteCommand; } - set { m_deleteCommand = (MySqlCommand)value; } - } #endregion /* * Implement abstract methods inherited from DbDataAdapter. */ + /// <summary> + /// Overridden. See <see cref="DbDataAdapter.CreateRowUpdatedEvent"/>. + /// </summary> + /// <param name="dataRow"></param> + /// <param name="command"></param> + /// <param name="statementType"></param> + /// <param name="tableMapping"></param> + /// <returns></returns> override protected RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) { return new MySqlRowUpdatedEventArgs(dataRow, command, statementType, tableMapping); } + /// <summary> + /// Overridden. See <see cref="DbDataAdapter.CreateRowUpdatingEvent"/>. + /// </summary> + /// <param name="dataRow"></param> + /// <param name="command"></param> + /// <param name="statementType"></param> + /// <param name="tableMapping"></param> + /// <returns></returns> override protected RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) { return new MySqlRowUpdatingEventArgs(dataRow, command, statementType, tableMapping); } + /// <summary> + /// Overridden. Raises the RowUpdating event. + /// </summary> + /// <param name="value">A MySqlRowUpdatingEventArgs that contains the event data.</param> override protected void OnRowUpdating(RowUpdatingEventArgs value) { MySqlRowUpdatingEventHandler handler = (MySqlRowUpdatingEventHandler) Events[EventRowUpdating]; @@ -137,6 +191,10 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Overridden. Raises the RowUpdated event. + /// </summary> + /// <param name="value">A MySqlRowUpdatedEventArgs that contains the event data. </param> override protected void OnRowUpdated(RowUpdatedEventArgs value) { MySqlRowUpdatedEventHandler handler = (MySqlRowUpdatedEventHandler) Events[EventRowUpdated]; @@ -146,12 +204,18 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Occurs during Update before a command is executed against the data source. The attempt to update is made, so the event fires. + /// </summary> public event MySqlRowUpdatingEventHandler RowUpdating { add { Events.AddHandler(EventRowUpdating, value); } remove { Events.RemoveHandler(EventRowUpdating, value); } } + /// <summary> + /// Occurs during Update after a command is executed against the data source. The attempt to update is made, so the event fires. + /// </summary> public event MySqlRowUpdatedEventHandler RowUpdated { add { Events.AddHandler(EventRowUpdated, value); } @@ -159,17 +223,36 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Represents the method that will handle the <see cref="MySqlDataAdapter.RowUpdating"/> event of a <see cref="MySqlDataAdapter"/>. + /// </summary> public delegate void MySqlRowUpdatingEventHandler(object sender, MySqlRowUpdatingEventArgs e); + + /// <summary> + /// Represents the method that will handle the <see cref="MySqlDataAdapter.RowUpdated"/> event of a <see cref="MySqlDataAdapter"/>. + /// </summary> public delegate void MySqlRowUpdatedEventHandler(object sender, MySqlRowUpdatedEventArgs e); - public class MySqlRowUpdatingEventArgs : RowUpdatingEventArgs + /// <summary> + /// Provides data for the RowUpdating event. This class cannot be inherited. + /// </summary> + public sealed class MySqlRowUpdatingEventArgs : RowUpdatingEventArgs { + /// <summary> + /// Initializes a new instance of the MySqlRowUpdatingEventArgs class. + /// </summary> + /// <param name="row">The <see cref="DataRow"/> to <see cref="DbDataAdapter.Update"/>.</param> + /// <param name="command">The <see cref="IDbCommand"/> to execute during <see cref="DbDataAdapter.Update"/>.</param> + /// <param name="statementType">One of the <see cref="StatementType"/> values that specifies the type of query executed.</param> + /// <param name="tableMapping">The <see cref="DataTableMapping"/> sent through an <see cref="DbDataAdapter.Update"/>.</param> public MySqlRowUpdatingEventArgs(DataRow row, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) : base(row, command, statementType, tableMapping) { } - // Hide the inherited implementation of the command property. + /// <summary> + /// Gets or sets the MySqlCommand to execute when performing the Update. + /// </summary> new public MySqlCommand Command { get { return (MySqlCommand)base.Command; } @@ -177,14 +260,26 @@ namespace ByteFX.Data.MySqlClient } } - public class MySqlRowUpdatedEventArgs : RowUpdatedEventArgs + /// <summary> + /// Provides data for the RowUpdated event. This class cannot be inherited. + /// </summary> + public sealed class MySqlRowUpdatedEventArgs : RowUpdatedEventArgs { + /// <summary> + /// Initializes a new instance of the MySqlRowUpdatedEventArgs class. + /// </summary> + /// <param name="row">The <see cref="DataRow"/> sent through an <see cref="DbDataAdapter.Update"/>.</param> + /// <param name="command">The <see cref="IDbCommand"/> executed when <see cref="DbDataAdapter.Update"/> is called.</param> + /// <param name="statementType">One of the <see cref="StatementType"/> values that specifies the type of query executed.</param> + /// <param name="tableMapping">The <see cref="DataTableMapping"/> sent through an <see cref="DbDataAdapter.Update"/>.</param> public MySqlRowUpdatedEventArgs(DataRow row, IDbCommand command, StatementType statementType, DataTableMapping tableMapping) : base(row, command, statementType, tableMapping) { } - // Hide the inherited implementation of the command property. + /// <summary> + /// Gets or sets the MySqlCommand executed when Update is called. + /// </summary> new public MySqlCommand Command { get { return (MySqlCommand)base.Command; } diff --git a/mcs/class/ByteFX.Data/mysqlclient/datareader.cs b/mcs/class/ByteFX.Data/mysqlclient/datareader.cs index 1f4a3c6272d..bc166c53253 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/datareader.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/datareader.cs @@ -21,6 +21,10 @@ using System.Collections; namespace ByteFX.Data.MySqlClient { + /// <summary> + /// Provides a means of reading a forward-only stream of rows from a MySQL database. This class cannot be inherited. + /// </summary> + /// <include file='docs/MySqlDataReader.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> public sealed class MySqlDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord { // The DataReader should always be open when returned to the user. @@ -33,7 +37,6 @@ namespace ByteFX.Data.MySqlClient private MySqlCommand command; private bool canRead; private bool hasRows; -// private Packet rowPacket = null; /* * Keep track of the connection in order to implement the @@ -54,14 +57,12 @@ namespace ByteFX.Data.MySqlClient commandBehavior = behavior; } - /**** - * METHODS / PROPERTIES FROM IDataReader. - ****/ + /// <summary> + /// Gets a value indicating the depth of nesting for the current row. This method is not + /// supported currently and always returns 0. + /// </summary> public int Depth { - /* - * Always return a value of zero if nesting is not supported. - */ get { return 0; } } @@ -73,7 +74,7 @@ namespace ByteFX.Data.MySqlClient get { return ! isOpen; } } - public void Dispose() + void IDisposable.Dispose() { if (isOpen) Close(); @@ -146,6 +147,10 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Gets the value of a column in its native format. + /// [C#] In C#, this property is the indexer for the MySqlDataReader class. + /// </summary> public object this [ String name ] { // Look up the ordinal and return @@ -183,13 +188,13 @@ namespace ByteFX.Data.MySqlClient /// <summary> /// Reads a stream of bytes from the specified column offset into the buffer an array starting at the given buffer offset. /// </summary> - /// <param name="i"></param> - /// <param name="fieldOffset"></param> - /// <param name="buffer"></param> - /// <param name="bufferoffset"></param> - /// <param name="length"></param> - /// <returns></returns> - public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) + /// <param name="i">The zero-based column ordinal. </param> + /// <param name="dataIndex">The index within the field from which to begin the read operation. </param> + /// <param name="buffer">The buffer into which to read the stream of bytes. </param> + /// <param name="bufferIndex">The index for buffer to begin the read operation. </param> + /// <param name="length">The maximum length to copy into the buffer. </param> + /// <returns>The actual number of bytes read.</returns> + public long GetBytes(int i, long dataIndex, byte[] buffer, int bufferIndex, int length) { if (i >= _fields.Length) throw new IndexOutOfRangeException(); @@ -199,15 +204,15 @@ namespace ByteFX.Data.MySqlClient if (buffer == null) return bytes.Length; - /// adjust the length so we don't run off the end - if (bytes.Length < (fieldOffset+length)) + // adjust the length so we don't run off the end + if (bytes.Length < (dataIndex+length)) { - length = (int)(bytes.Length - fieldOffset); + length = (int)(bytes.Length - dataIndex); } for (int x=0; x < length; x++) { - buffer[bufferoffset+x] = bytes[fieldOffset+x]; + buffer[bufferIndex+x] = bytes[dataIndex+x]; } return length; @@ -247,7 +252,7 @@ namespace ByteFX.Data.MySqlClient if (buffer == null) return chars.Length; - /// adjust the length so we don't run off the end + // adjust the length so we don't run off the end if (chars.Length < (fieldOffset+length)) { length = (int)(chars.Length - fieldOffset); @@ -547,13 +552,8 @@ namespace ByteFX.Data.MySqlClient #endregion - public IDataReader GetData(int i) + IDataReader IDataRecord.GetData(int i) { - /* - * The sample code does not support this method. Normally, - * this would be used to expose nested tables and - * other hierarchical data. - */ throw new NotSupportedException("GetData not supported."); } @@ -578,13 +578,19 @@ namespace ByteFX.Data.MySqlClient Driver driver = connection.InternalConnection.Driver; + // clear any rows that have not been read from the last rowset ClearCurrentResult(); - // tell our command to execute the next sql batch + // tell our command to continue execution of the SQL batch until it its + // another resultset Packet packet = command.ExecuteBatch(true); - // if there was no more batches, then signal done - if (packet == null) return false; + // if there was no more resultsets, then signal done + if (packet == null) + { + canRead = false; + return false; + } // When executing query statements, the result byte that is returned // from MySql is the column count. That is why we reference the LastResult @@ -594,14 +600,20 @@ namespace ByteFX.Data.MySqlClient _fields = new MySqlField[ packet.ReadLenInteger() ]; for (int x=0; x < _fields.Length; x++) { + packet = driver.ReadPacket(); _fields[x] = new MySqlField(); _fields[x].ReadSchemaInfo( packet ); } + // read off the end of schema packet + packet = driver.ReadPacket(); + if ( ! packet.IsLastPacket()) + throw new MySqlException("Expected end of schema packet"); + // now take a quick peek at the next packet to see if we have rows // packet = driver.PeekPacket(); - hasRows = packet.Type != PacketType.Last; + hasRows = ! packet.IsLastPacket(); canRead = hasRows; connection.SetState( ConnectionState.Open ); @@ -625,7 +637,7 @@ namespace ByteFX.Data.MySqlClient try { Packet rowPacket = driver.ReadPacket(); - if (rowPacket.Type == PacketType.Last) + if (rowPacket.IsLastPacket()) { canRead = false; return false; @@ -665,16 +677,18 @@ namespace ByteFX.Data.MySqlClient { if (! canRead) return; - Packet packet = connection.InternalConnection.Driver.ReadPacket(); + Driver driver = connection.InternalConnection.Driver; + + Packet packet = driver.ReadPacket(); // clean out any current resultset - while (packet.Type != PacketType.Last) - packet = connection.InternalConnection.Driver.ReadPacket(); + while (! packet.IsLastPacket()) + packet = driver.ReadPacket(); } #endregion #region IEnumerator - public IEnumerator GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { return new System.Data.Common.DbEnumerator(this); } diff --git a/mcs/class/ByteFX.Data/mysqlclient/parameter.cs b/mcs/class/ByteFX.Data/mysqlclient/parameter.cs index 7bcc2552801..8ce6aed3d24 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/parameter.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/parameter.cs @@ -24,25 +24,37 @@ using System.Reflection; namespace ByteFX.Data.MySqlClient { + /// <summary> + /// Represents a parameter to a <see cref="MySqlCommand"/>, and optionally, its mapping to <see cref="DataSet"/> columns. This class cannot be inherited. + /// </summary> [TypeConverter(typeof(MySqlParameter.MySqlParameterConverter))] public sealed class MySqlParameter : MarshalByRefObject, IDataParameter, IDbDataParameter, ICloneable { - MySqlDbType dbType = MySqlDbType.Null; - DbType genericType; - ParameterDirection direction = ParameterDirection.Input; - bool isNullable = false; - string paramName; - string sourceColumn; - DataRowVersion sourceVersion = DataRowVersion.Current; - object paramValue = DBNull.Value; - int size; - byte precision=0, scale=0; + private MySqlDbType dbType = MySqlDbType.Null; + private DbType genericType; + private ParameterDirection direction = ParameterDirection.Input; + private bool isNullable = false; + private string paramName; + private string sourceColumn; + private DataRowVersion sourceVersion = DataRowVersion.Current; + private object paramValue = DBNull.Value; + private int size; + private byte precision=0, scale=0; #region Constructors + + /// <summary> + /// Initializes a new instance of the MySqlParameter class. + /// </summary> public MySqlParameter() { } + /// <summary> + /// Initializes a new instance of the <see cref="MySqlParameter"/> class with the parameter name and a value of the new MySqlParameter. + /// </summary> + /// <param name="parameterName">The name of the parameter to map. </param> + /// <param name="value">An <see cref="Object"/> that is the value of the <see cref="MySqlParameter"/>. </param> public MySqlParameter(string parameterName, object value) { ParameterName = parameterName; @@ -51,34 +63,54 @@ namespace ByteFX.Data.MySqlClient genericType = GetGenericType( paramValue.GetType() ); } - public MySqlParameter( string parameterName, MySqlDbType type) + /// <summary> + /// Initializes a new instance of the <see cref="MySqlParameter"/> class with the parameter name and the data type. + /// </summary> + /// <param name="parameterName">The name of the parameter to map. </param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + public MySqlParameter( string parameterName, MySqlDbType dbType) { ParameterName = parameterName; - dbType = type; + this.dbType = dbType; } - public MySqlParameter( string parameterName, MySqlDbType type, int size ) + /// <summary> + /// Initializes a new instance of the <see cref="MySqlParameter"/> class with the parameter name, the <see cref="MySqlDbType"/>, and the size. + /// </summary> + /// <param name="parameterName">The name of the parameter to map. </param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + /// <param name="size">The length of the parameter. </param> + public MySqlParameter( string parameterName, MySqlDbType dbType, int size ) { ParameterName = parameterName; - dbType = type; + this.dbType = dbType; this.size = size; } - public MySqlParameter( string name, MySqlDbType dbType, int size, string sourceCol ) + /// <summary> + /// Initializes a new instance of the <see cref="MySqlParameter"/> class with the parameter name, the <see cref="MySqlDbType"/>, the size, and the source column name. + /// </summary> + /// <param name="parameterName">The name of the parameter to map. </param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + /// <param name="size">The length of the parameter. </param> + /// <param name="sourceColumn">The name of the source column. </param> + public MySqlParameter( string parameterName, MySqlDbType dbType, int size, string sourceColumn ) { - ParameterName = name; + ParameterName = parameterName; this.dbType = dbType; this.size = size; this.direction = ParameterDirection.Input; this.precision = 0; this.scale = 0; - this.sourceColumn = sourceCol; + this.sourceColumn = sourceColumn; this.sourceVersion = DataRowVersion.Current; this.paramValue =null; } - public MySqlParameter(string name, MySqlDbType type, ParameterDirection dir, string col, DataRowVersion ver, object val) + internal MySqlParameter(string name, MySqlDbType type, ParameterDirection dir, string col, DataRowVersion ver, object val) { + if (direction != ParameterDirection.Input) + throw new ArgumentException("Only input parameters are supported by MySql"); dbType = type; direction = dir; ParameterName = name; @@ -87,10 +119,27 @@ namespace ByteFX.Data.MySqlClient paramValue = val; } + /// <summary> + /// Initializes a new instance of the <see cref="MySqlParameter"/> class with the parameter name, the type of the parameter, the size of the parameter, a <see cref="ParameterDirection"/>, the precision of the parameter, the scale of the parameter, the source column, a <see cref="DataRowVersion"/> to use, and the value of the parameter. + /// </summary> + /// <param name="parameterName">The name of the parameter to map. </param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + /// <param name="size">The length of the parameter. </param> + /// <param name="direction">One of the <see cref="ParameterDirection"/> values. </param> + /// <param name="isNullable">true if the value of the field can be null, otherwise false. </param> + /// <param name="precision">The total number of digits to the left and right of the decimal point to which <see cref="MySqlParameter.Value"/> is resolved.</param> + /// <param name="scale">The total number of decimal places to which <see cref="MySqlParameter.Value"/> is resolved. </param> + /// <param name="sourceColumn">The name of the source column. </param> + /// <param name="sourceVersion">One of the <see cref="DataRowVersion"/> values. </param> + /// <param name="value">An <see cref="Object"/> that is the value of the <see cref="MySqlParameter"/>. </param> + /// <exception cref="ArgumentException"/> public MySqlParameter( string parameterName, MySqlDbType dbType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, object value) { + if (direction != ParameterDirection.Input) + throw new ArgumentException("Only input parameters are supported by MySql"); + ParameterName = parameterName; this.dbType = dbType; this.size = size; @@ -104,6 +153,10 @@ namespace ByteFX.Data.MySqlClient #endregion #region Properties + + /// <summary> + /// Gets or sets the <see cref="DbType"/> of the parameter. + /// </summary> public DbType DbType { get @@ -133,11 +186,11 @@ namespace ByteFX.Data.MySqlClient case DbType.Int32: case DbType.UInt32: - MySqlDbType = MySqlDbType.Long; break; + MySqlDbType = MySqlDbType.Int; break; case DbType.Int64: case DbType.UInt64: - MySqlDbType = MySqlDbType.LongLong; break; + MySqlDbType = MySqlDbType.BigInt; break; case DbType.DateTime: MySqlDbType = MySqlDbType.Datetime; break; @@ -165,19 +218,10 @@ namespace ByteFX.Data.MySqlClient } } - [Category("Data")] - public MySqlDbType MySqlDbType - { - get - { - return dbType; - } - set - { - dbType = value; - } - } - + /// <summary> + /// Gets or sets a value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value parameter. + /// As of MySql version 4.1 and earlier, input-only is the only valid choice. + /// </summary> [Category("Data")] public ParameterDirection Direction { @@ -185,12 +229,34 @@ namespace ByteFX.Data.MySqlClient set { direction = value; } } + /// <summary> + /// Gets or sets a value indicating whether the parameter accepts null values. + /// </summary> [Browsable(false)] public Boolean IsNullable { get { return isNullable; } } + /// <summary> + /// Gets or sets the MySqlDbType of the parameter. + /// </summary> + [Category("Data")] + public MySqlDbType MySqlDbType + { + get + { + return dbType; + } + set + { + dbType = value; + } + } + + /// <summary> + /// Gets or sets the name of the MySqlParameter. + /// </summary> [Category("Misc")] public String ParameterName { @@ -203,6 +269,39 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Gets or sets the maximum number of digits used to represent the <see cref="Value"/> property. + /// </summary> + [Category("Data")] + public byte Precision + { + get { return precision; } + set { precision = value; } + } + + /// <summary> + /// Gets or sets the number of decimal places to which <see cref="Value"/> is resolved. + /// </summary> + [Category("Data")] + public byte Scale + { + get { return scale; } + set { scale = value; } + } + + /// <summary> + /// Gets or sets the maximum size, in bytes, of the data within the column. + /// </summary> + [Category("Data")] + public int Size + { + get { return size; } + set { size = value; } + } + + /// <summary> + /// Gets or sets the name of the source column that is mapped to the <see cref="DataSet"/> and used for loading or returning the <see cref="Value"/>. + /// </summary> [Category("Data")] public String SourceColumn { @@ -210,6 +309,9 @@ namespace ByteFX.Data.MySqlClient set { sourceColumn = value; } } + /// <summary> + /// Gets or sets the <see cref="DataRowVersion"/> to use when loading <see cref="Value"/>. + /// </summary> [Category("Data")] public DataRowVersion SourceVersion { @@ -217,6 +319,9 @@ namespace ByteFX.Data.MySqlClient set { sourceVersion = value; } } + /// <summary> + /// Gets or sets the value of the parameter. + /// </summary> [TypeConverter(typeof(StringConverter))] [Category("Data")] public object Value @@ -233,27 +338,6 @@ namespace ByteFX.Data.MySqlClient } } - // implement methods of IDbDataParameter - [Category("Data")] - public byte Precision - { - get { return precision; } - set { precision = value; } - } - - [Category("Data")] - public byte Scale - { - get { return scale; } - set { scale = value; } - } - - [Category("Data")] - public int Size - { - get { return size; } - set { size = value; } - } #endregion private void EscapeByteArray( byte[] bytes, System.IO.MemoryStream s ) @@ -279,6 +363,10 @@ namespace ByteFX.Data.MySqlClient s.Write( newbytes, 0, newx ); } + /// <summary> + /// Overridden. Gets a string containing the <see cref="ParameterName"/>. + /// </summary> + /// <returns></returns> public override string ToString() { return paramName; @@ -297,7 +385,7 @@ namespace ByteFX.Data.MySqlClient return sb.ToString(); } - public void SerializeToBytes( System.IO.MemoryStream s, MySqlConnection conn ) + internal void SerializeToBytes( System.IO.MemoryStream s, MySqlConnection conn ) { string parm_string = null; byte[] bytes = null; @@ -424,9 +512,9 @@ namespace ByteFX.Data.MySqlClient case TypeCode.Int16: case TypeCode.UInt16: return MySqlDbType.Int24; case TypeCode.Int32: - case TypeCode.UInt32: return MySqlDbType.Long; + case TypeCode.UInt32: return MySqlDbType.Int; case TypeCode.Int64: - case TypeCode.UInt64: return MySqlDbType.LongLong; + case TypeCode.UInt64: return MySqlDbType.BigInt; case TypeCode.Single: return MySqlDbType.Float; case TypeCode.Double: return MySqlDbType.Double; case TypeCode.Decimal: return MySqlDbType.Decimal; @@ -440,7 +528,7 @@ namespace ByteFX.Data.MySqlClient #region ICloneable - public object Clone() + object System.ICloneable.Clone() { MySqlParameter clone = new MySqlParameter( paramName, dbType, direction, sourceColumn, sourceVersion, paramValue ); diff --git a/mcs/class/ByteFX.Data/mysqlclient/parameter_collection.cs b/mcs/class/ByteFX.Data/mysqlclient/parameter_collection.cs index ecaadd0be26..84995140a4a 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/parameter_collection.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/parameter_collection.cs @@ -22,6 +22,10 @@ using System.ComponentModel; namespace ByteFX.Data.MySqlClient { + /// <summary> + /// Represents a collection of parameters relevant to a <see cref="MySqlCommand"/> as well as their respective mappings to columns in a <see cref="DataSet"/>. This class cannot be inherited. + /// </summary> + /// <include file='docs/MySqlParameterCollection.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> [Editor(typeof(ByteFX.Data.Common.DBParametersEditor), typeof(System.Drawing.Design.UITypeEditor))] [ListBindable(true)] public sealed class MySqlParameterCollection : MarshalByRefObject, IDataParameterCollection, @@ -49,12 +53,12 @@ namespace ByteFX.Data.MySqlClient _parms.CopyTo(array, index); } - public bool IsSynchronized + bool ICollection.IsSynchronized { get { return _parms.IsSynchronized; } } - public object SyncRoot + object ICollection.SyncRoot { get { return _parms.SyncRoot; } } @@ -70,11 +74,23 @@ namespace ByteFX.Data.MySqlClient _parms.Clear(); } + /// <summary> + /// Gets a value indicating whether a MySqlParameter exists in the collection. + /// </summary> + /// <param name="value">The value of the <see cref="MySqlParameter"/> object to find. </param> + /// <returns>true if the collection contains the <see cref="MySqlParameter"/> object; otherwise, false.</returns> + /// <overloads>Gets a value indicating whether a <see cref="MySqlParameter"/> exists in the collection.</overloads> public bool Contains(object value) { return _parms.Contains(value); } + /// <summary> + /// Gets the location of a <see cref="MySqlParameter"/> in the collection. + /// </summary> + /// <param name="value">The <see cref="MySqlParameter"/> object to locate. </param> + /// <returns>The zero-based location of the <see cref="MySqlParameter"/> in the collection.</returns> + /// <overloads>Gets the location of a <see cref="MySqlParameter"/> in the collection.</overloads> public int IndexOf(object value) { return _parms.IndexOf(value); @@ -90,12 +106,12 @@ namespace ByteFX.Data.MySqlClient _parms.Insert( index, value ); } - public bool IsFixedSize + bool IList.IsFixedSize { get { return _parms.IsFixedSize; } } - public bool IsReadOnly + bool IList.IsReadOnly { get { return _parms.IsReadOnly; } } @@ -109,6 +125,11 @@ namespace ByteFX.Data.MySqlClient _parms.Remove( value ); } + /// <summary> + /// Removes the specified <see cref="MySqlParameter"/> from the collection using a specific index. + /// </summary> + /// <param name="index">The zero-based index of the parameter. </param> + /// <overloads>Removes the specified <see cref="MySqlParameter"/> from the collection.</overloads> public void RemoveAt( int index ) { _parms.RemoveAt( index ); @@ -124,6 +145,11 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Adds the specified <see cref="MySqlParameter"/> object to the <see cref="MySqlParameterCollection"/>. + /// </summary> + /// <param name="value">The <see cref="MySqlParameter"/> to add to the collection.</param> + /// <returns>The index of the new <see cref="MySqlParameter"/> object.</returns> public int Add( object value ) { if (! (value is MySqlParameter)) throw new MySqlException("Only MySqlParameter objects may be stored"); @@ -139,6 +165,12 @@ namespace ByteFX.Data.MySqlClient #endregion #region IDataParameterCollection + + /// <summary> + /// Gets a value indicating whether a <see cref="MySqlParameter"/> with the specified parameter name exists in the collection. + /// </summary> + /// <param name="name">The name of the <see cref="MySqlParameter"/> object to find.</param> + /// <returns>true if the collection contains the parameter; otherwise, false.</returns> public bool Contains(string name) { if (name[0] == '@') @@ -150,18 +182,27 @@ namespace ByteFX.Data.MySqlClient return false; } - public int IndexOf( string name ) + /// <summary> + /// Gets the location of the <see cref="MySqlParameter"/> in the collection with a specific parameter name. + /// </summary> + /// <param name="parameterName">The name of the <see cref="MySqlParameter"/> object to retrieve. </param> + /// <returns>The zero-based location of the <see cref="MySqlParameter"/> in the collection.</returns> + public int IndexOf( string parameterName ) { - if (name[0] == '@') - name = name.Substring(1, name.Length-1); + if (parameterName[0] == '@') + parameterName = parameterName.Substring(1, parameterName.Length-1); for (int x=0; x < _parms.Count; x++) { MySqlParameter p = (MySqlParameter)_parms[x]; - if (p.ParameterName.ToLower().Equals( name.ToLower() )) return x; + if (p.ParameterName.ToLower().Equals( parameterName.ToLower() )) return x; } - throw new MySqlException("Parameter '" + name + "' not found in collection"); + throw new MySqlException("Parameter '" + parameterName + "' not found in collection"); } + /// <summary> + /// Removes the specified <see cref="MySqlParameter"/> from the collection using the parameter name. + /// </summary> + /// <param name="name">The name of the <see cref="MySqlParameter"/> object to retrieve. </param> public void RemoveAt( string name ) { int index = IndexOf( name ); @@ -180,25 +221,40 @@ namespace ByteFX.Data.MySqlClient #endregion #region IEnumerable - public IEnumerator GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_parms).GetEnumerator(); } #endregion #region Public Methods + + /// <summary> + /// Gets the <see cref="MySqlParameter"/> at the specified index. + /// </summary> + /// <overloads>Gets the <see cref="MySqlParameter"/> with a specified attribute. + /// [C#] In C#, this property is the indexer for the <see cref="MySqlParameterCollection"/> class. + /// </overloads> public MySqlParameter this[int index] { get { return (MySqlParameter)_parms[index]; } set { _parms[index] = value; } } + /// <summary> + /// Gets the <see cref="MySqlParameter"/> with the specified name. + /// </summary> public MySqlParameter this[string name] { get { return (MySqlParameter)_parms[ IndexOf( name ) ]; } set { _parms[ IndexOf( name ) ] = value; } } + /// <summary> + /// Adds the specified <see cref="MySqlParameter"/> object to the <see cref="MySqlParameterCollection"/>. + /// </summary> + /// <param name="value">The <see cref="MySqlParameter"/> to add to the collection.</param> + /// <returns>The index of the new <see cref="MySqlParameter"/> object.</returns> public MySqlParameter Add(MySqlParameter value) { if ( value.ParameterName == null ) throw new ArgumentException("parameter must be named"); @@ -207,21 +263,48 @@ namespace ByteFX.Data.MySqlClient return value; } + /// <summary> + /// Adds a <see cref="MySqlParameter"/> to the <see cref="MySqlParameterCollection"/> given the specified parameter name and value. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="MySqlParameter.Value"/> of the <see cref="MySqlParameter"/> to add to the collection.</param> + /// <returns>The index of the new <see cref="MySqlParameter"/> object.</returns> public MySqlParameter Add( string parameterName, object value ) { return Add( new MySqlParameter( parameterName, value ) ); } - public MySqlParameter Add(string parameterName, MySqlDbType type) + /// <summary> + /// Adds a <see cref="MySqlParameter"/> to the <see cref="MySqlParameterCollection"/> given the parameter name and the data type. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + /// <returns>The index of the new <see cref="MySqlParameter"/> object.</returns> + public MySqlParameter Add(string parameterName, MySqlDbType dbType) { - return Add(new MySqlParameter(parameterName, type)); + return Add(new MySqlParameter(parameterName, dbType)); } + /// <summary> + /// Adds a <see cref="MySqlParameter"/> to the <see cref="MySqlParameterCollection"/> with the parameter name, the data type, and the column length. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + /// <param name="size">The length of the column.</param> + /// <returns>The index of the new <see cref="MySqlParameter"/> object.</returns> public MySqlParameter Add(string parameterName, MySqlDbType dbType, int size) { return Add(new MySqlParameter(parameterName, dbType, size )); } + /// <summary> + /// Adds a <see cref="MySqlParameter"/> to the <see cref="MySqlParameterCollection"/> with the parameter name, the data type, the column length, and the source column name. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="MySqlDbType"/> values. </param> + /// <param name="size">The length of the column.</param> + /// <param name="sourceColumn">The name of the source column.</param> + /// <returns>The index of the new <see cref="MySqlParameter"/> object.</returns> public MySqlParameter Add(string parameterName, MySqlDbType dbType, int size, string sourceColumn) { return Add(new MySqlParameter(parameterName, dbType, size, sourceColumn)); diff --git a/mcs/class/ByteFX.Data/mysqlclient/transcaction.cs b/mcs/class/ByteFX.Data/mysqlclient/transcaction.cs index f52a74c4620..213dac7818d 100755 --- a/mcs/class/ByteFX.Data/mysqlclient/transcaction.cs +++ b/mcs/class/ByteFX.Data/mysqlclient/transcaction.cs @@ -20,7 +20,11 @@ using System.Data; namespace ByteFX.Data.MySqlClient { - public class MySqlTransaction : IDbTransaction + /// <summary> + /// Represents a SQL transaction to be made in a MySQL database. This class cannot be inherited. + /// </summary> + /// <include file='docs/MySqlTransaction.xml' path='MyDocs/MyMembers[@name="Class"]/*'/> + public sealed class MySqlTransaction : IDbTransaction { private IsolationLevel _level; private MySqlConnection _conn; @@ -31,22 +35,35 @@ namespace ByteFX.Data.MySqlClient _open = true; } - public IsolationLevel IsolationLevel - { - get { return _level; } - set { _level = value; } - } + #region Properties + /// <summary> + /// Gets the <see cref="MySqlConnection"/> object associated with the transaction, or a null reference (Nothing in Visual Basic) if the transaction is no longer valid. + /// </summary> public IDbConnection Connection { get { return _conn; } set { _conn = (MySqlConnection)value; } } - public void Dispose() + /// <summary> + /// Specifies the <see cref="IsolationLevel"/> for this transaction. + /// </summary> + public IsolationLevel IsolationLevel + { + get { return _level; } + set { _level = value; } + } + + #endregion + + void System.IDisposable.Dispose() { } + /// <summary> + /// Commits the database transaction. + /// </summary> public void Commit() { if (_conn == null || _conn.State != ConnectionState.Open) @@ -65,6 +82,9 @@ namespace ByteFX.Data.MySqlClient } } + /// <summary> + /// Overloaded. Rolls back a transaction from a pending state. + /// </summary> public void Rollback() { if (_conn == null || _conn.State != ConnectionState.Open) diff --git a/mcs/class/Microsoft.JScript/Microsoft.JScript/ChangeLog b/mcs/class/Microsoft.JScript/Microsoft.JScript/ChangeLog index fdb5bc41d5a..cad0fd09a7b 100644 --- a/mcs/class/Microsoft.JScript/Microsoft.JScript/ChangeLog +++ b/mcs/class/Microsoft.JScript/Microsoft.JScript/ChangeLog @@ -1,3 +1,16 @@ +2003-11-30 Cesar Lopez Nataren <cesar@ciencias.unam.mx> + + * expression.cs: Added new field to Call class, so it can handle more arguments. + + * StringLiteral.cs (Emit) : Load the string value. + + * Literal.cs: BooleanLiteral, Resolve always return true. Emit the value and box it to Boolean. + + * CodeGenerator.cs: Set parent if 'JScript 0' to GlobalScope. Set + custom attribute. Build default 'JScript 0' constructor. Emit + default initial/final code of 'Global Code'. Create default + 'JScript Main'. + 2003-11-29 Cesar Lopez Nataren <cesar@ciencias.unam.mx> * jscript-lexer-grammar.g: define new rule 'arguments' instead of diff --git a/mcs/class/Microsoft.JScript/Microsoft.JScript/CodeGenerator.cs b/mcs/class/Microsoft.JScript/Microsoft.JScript/CodeGenerator.cs index 781c03f5468..396ec8af05b 100644 --- a/mcs/class/Microsoft.JScript/Microsoft.JScript/CodeGenerator.cs +++ b/mcs/class/Microsoft.JScript/Microsoft.JScript/CodeGenerator.cs @@ -11,6 +11,8 @@ using System; using System.Reflection; using System.Reflection.Emit; using System.Threading; +using Microsoft.JScript.Vsa; +using System.Runtime.CompilerServices; namespace Microsoft.JScript { @@ -78,8 +80,18 @@ namespace Microsoft.JScript { TypeBuilder type_builder; type_builder = module_builder.DefineType ("JScript 0"); + + type_builder.SetParent (typeof (GlobalScope)); + type_builder.SetCustomAttribute (new CustomAttributeBuilder + (typeof (CompilerGlobalScopeAttribute).GetConstructor (new Type [] {}), new object [] {})); + EmitContext ec = new EmitContext (type_builder); + // + // Build the default constructor of the type + // + emit_default_script_constructor (ec); + // here we only emit code for declarations. prog.Emit (ec); @@ -96,18 +108,129 @@ namespace Microsoft.JScript { ec.ig = method.GetILGenerator (); ec.is_global_code_method = true; - prog.Emit (ec); + emit_default_init_global_code (ec.ig); + prog.Emit (ec); + emit_default_end_global_code (ec.ig); ec.type_builder.CreateType (); + + // + // Build the default 'JScript Main' class + // + ec.type_builder = module_builder.DefineType ("JScript Main"); + emit_jscript_main (ec.type_builder); + ec.type_builder.CreateType (); + } + + internal static void emit_default_init_global_code (ILGenerator ig) + { + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine")); + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Call, + typeof (VsaEngine).GetMethod ("PushScriptObject", + new Type [] { typeof (ScriptObject)})); + } + + internal static void emit_default_end_global_code (ILGenerator ig) + { + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine")); + ig.Emit (OpCodes.Call, typeof (VsaEngine).GetMethod ("PopScriptObject")); + ig.Emit (OpCodes.Pop); + ig.Emit (OpCodes.Ret); + } + + internal static void emit_default_script_constructor (EmitContext ec) + { + ConstructorBuilder cons_builder; + TypeBuilder tb = ec.type_builder; + cons_builder = tb.DefineConstructor (MethodAttributes.Public, + CallingConventions.Standard, + new Type [] { typeof (GlobalScope) }); + + ILGenerator ig = cons_builder.GetILGenerator (); + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldarg_1); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Ldfld, + typeof (ScriptObject).GetField ("engine")); + + ig.Emit (OpCodes.Call, + typeof (GlobalScope).GetConstructor (new Type [] {typeof (GlobalScope), + typeof (VsaEngine)})); + ig.Emit (OpCodes.Ret); + } + + internal static void emit_jscript_main (TypeBuilder tb) + { + emit_jscript_main_constructor (tb); + emit_jscript_main_entry_point (tb); + } + + internal static void emit_jscript_main_constructor (TypeBuilder tb) + { + ConstructorBuilder cons = tb.DefineConstructor (MethodAttributes.Public, + CallingConventions.Standard, + new Type [] {}); + ILGenerator ig = cons.GetILGenerator (); + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Call, typeof (Object).GetConstructor (new Type [] {})); + ig.Emit (OpCodes.Ret); + } + + internal static void emit_jscript_main_entry_point (TypeBuilder tb) + { + MethodBuilder method; + method = tb.DefineMethod ("Main", + MethodAttributes.Public | MethodAttributes.Static, + typeof (void), new Type [] {typeof (String [])}); + + method.SetCustomAttribute (new CustomAttributeBuilder + (typeof (STAThreadAttribute).GetConstructor ( + new Type [] {}), + new object [] {})); + + ILGenerator ig = method.GetILGenerator (); + + ig.DeclareLocal (typeof (GlobalScope)); + + ig.Emit (OpCodes.Ldc_I4_1); + ig.Emit (OpCodes.Ldc_I4_1); + ig.Emit (OpCodes.Newarr, typeof (string)); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Ldc_I4_0); + + ig.Emit (OpCodes.Ldstr, + "mscorlib, Version=1.0.3300.0, Culture=neutral, Pub" + + "licKeyToken=b77a5c561934e089"); + + ig.Emit (OpCodes.Stelem_Ref); + + ig.Emit (OpCodes.Call, + typeof (VsaEngine).GetMethod ("CreateEngineAndGetGlobalScope", + new Type [] {typeof (bool), + typeof (string [])})); + ig.Emit (OpCodes.Stloc_0); + ig.Emit (OpCodes.Ldloc_0); + + ig.Emit (OpCodes.Newobj, + assembly_builder.GetType ("JScript 0").GetConstructor ( + new Type [] {typeof (GlobalScope)})); + ig.Emit (OpCodes.Call, + assembly_builder.GetType ("JScript 0").GetMethod ( + "Global Code", new Type [] {})); + ig.Emit (OpCodes.Pop); + ig.Emit (OpCodes.Ret); + + assembly_builder.SetEntryPoint (method); } public static void Run (string file_name, AST prog) { CodeGenerator.Init (file_name); CodeGenerator.Emit (prog); - - CodeGenerator.Save (trim_extension (file_name) + - ".exe"); + CodeGenerator.Save (trim_extension (file_name) + ".exe"); } } } diff --git a/mcs/class/Microsoft.JScript/Microsoft.JScript/Literal.cs b/mcs/class/Microsoft.JScript/Microsoft.JScript/Literal.cs index 303708e6a0b..2749c604f46 100644 --- a/mcs/class/Microsoft.JScript/Microsoft.JScript/Literal.cs +++ b/mcs/class/Microsoft.JScript/Microsoft.JScript/Literal.cs @@ -8,6 +8,7 @@ // using System; +using System.Reflection.Emit; namespace Microsoft.JScript { @@ -40,12 +41,19 @@ namespace Microsoft.JScript { internal override bool Resolve (IdentificationTable context) { - throw new NotImplementedException (); + return true; } internal override void Emit (EmitContext ec) { - throw new NotImplementedException (); + if (ec.is_global_code_method) { + if (val) + ec.ig.Emit (OpCodes.Ldc_I4_1); + else + ec.ig.Emit (OpCodes.Ldc_I4_0); + + ec.ig.Emit (OpCodes.Box, typeof (System.Boolean)); + } } } diff --git a/mcs/class/Microsoft.JScript/Microsoft.JScript/StringLiteral.cs b/mcs/class/Microsoft.JScript/Microsoft.JScript/StringLiteral.cs index 9aa1984b2b8..bc2eeb9d617 100644 --- a/mcs/class/Microsoft.JScript/Microsoft.JScript/StringLiteral.cs +++ b/mcs/class/Microsoft.JScript/Microsoft.JScript/StringLiteral.cs @@ -8,6 +8,7 @@ // using System; +using System.Reflection.Emit; namespace Microsoft.JScript { @@ -37,7 +38,9 @@ namespace Microsoft.JScript { internal override void Emit (EmitContext ec) { - throw new NotImplementedException (); + if (ec.is_global_code_method) { + ec.ig.Emit (OpCodes.Ldstr, str); + } } } } diff --git a/mcs/class/Microsoft.JScript/Microsoft.JScript/expression.cs b/mcs/class/Microsoft.JScript/Microsoft.JScript/expression.cs index 35d3fc162e8..81d3e16616a 100755 --- a/mcs/class/Microsoft.JScript/Microsoft.JScript/expression.cs +++ b/mcs/class/Microsoft.JScript/Microsoft.JScript/expression.cs @@ -29,21 +29,24 @@ namespace Microsoft.JScript { if (oper != JSToken.None) sb.Append (oper + " "); - sb.Append (operand.ToString ()); + if (operand != null) + sb.Append (operand.ToString ()); return sb.ToString (); } internal override bool Resolve (IdentificationTable context) { - operand.Resolve (context); + if (operand != null) + operand.Resolve (context); return true; } internal override void Emit (EmitContext ec) { - throw new NotImplementedException (); + if (operand != null) + operand.Emit (ec); } } @@ -144,34 +147,41 @@ namespace Microsoft.JScript { public class Call : AST { - internal AST left; - internal AST args; + internal AST member_exp; + internal AST args1; + internal AST args2; - public Call (AST left, AST args) + public Call (AST member_exp, AST args1, AST args2) { - this.left = left; - this.args = args; + this.member_exp = member_exp; + this.args1 = args1; + this.args2 = args2; } public override string ToString () { StringBuilder sb = new StringBuilder (); - if (left != null) - sb.Append (left.ToString () + " "); - if (args != null) - sb.Append (args.ToString ()); + if (member_exp != null) + sb.Append (member_exp.ToString () + " "); + if (args1 != null) + sb.Append (args1.ToString ()); + if (args2 != null) + sb.Append (args2.ToString ()); return sb.ToString (); } internal override bool Resolve (IdentificationTable context) { - if (left != null) - left.Resolve (context); + if (member_exp != null) + member_exp.Resolve (context); + + if (args1 != null) + args1.Resolve (context); - if (args != null) - args.Resolve (context); + if (args2 != null) + args2.Resolve (context); return true; } diff --git a/mcs/class/Mono.Security/Mono.Security.Cryptography/MD5SHA1CryptoServiceProvider.cs b/mcs/class/Mono.Security/Mono.Security.Cryptography/MD5SHA1CryptoServiceProvider.cs new file mode 100755 index 00000000000..bf97dfd3a82 --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Cryptography/MD5SHA1CryptoServiceProvider.cs @@ -0,0 +1,127 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Security.Cryptography; + +namespace Mono.Security.Cryptography +{ + internal class MD5SHA1CryptoServiceProvider : HashAlgorithm + { + #region FIELDS + + private HashAlgorithm md5; + private HashAlgorithm sha; + private bool hashing; + + #endregion + + #region CONSTRUCTORS + + public MD5SHA1CryptoServiceProvider() + { + this.md5 = new MD5CryptoServiceProvider(); + this.sha = new SHA1CryptoServiceProvider(); + + // Set HashSizeValue + this.HashSizeValue = this.md5.HashSize + this.sha.HashSize; + } + + #endregion + + #region METHODS + + public override void Initialize() + { + this.md5.Initialize(); + this.sha.Initialize(); + this.hashing = false; + } + + protected override byte[] HashFinal() + { + if (!hashing) + { + this.hashing = true; + } + // Finalize the original hash + this.md5.TransformFinalBlock(new byte[0], 0, 0); + this.sha.TransformFinalBlock(new byte[0], 0, 0); + + byte[] hash = new byte[36]; + + System.Array.Copy(this.md5.Hash, 0, hash, 0, 16); + System.Array.Copy(this.sha.Hash, 0, hash, 16, 20); + + return hash; + } + + protected override void HashCore( + byte[] array, + int ibStart, + int cbSize) + { + if (!hashing) + { + hashing = true; + } + this.md5.TransformBlock(array, ibStart, cbSize, array, ibStart); + this.sha.TransformBlock(array, ibStart, cbSize, array, ibStart); + } + + public byte[] CreateSignature(RSA rsa) + { + if (rsa == null) + { + throw new CryptographicUnexpectedOperationException ("missing key"); + } + + #warning "MD5SHA1 hash is not supported by .NET" + RSAPKCS1SignatureFormatter f = new RSAPKCS1SignatureFormatter(rsa); + f.SetHashAlgorithm("MD5SHA1"); + + return f.CreateSignature(this.Hash); + } + + public bool VerifySignature(RSA rsa, byte[] rgbSignature) + { + if (rsa == null) + { + throw new CryptographicUnexpectedOperationException ("missing key"); + } + if (rgbSignature == null) + { + throw new ArgumentNullException ("rgbSignature"); + } + + #warning "MD5SHA1 hash is not supported by .NET" + RSAPKCS1SignatureDeformatter d = new RSAPKCS1SignatureDeformatter(rsa); + d.SetHashAlgorithm("MD5SHA1"); + + return d.VerifySignature(this.Hash, rgbSignature); + } + + #endregion + } +} diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsCompressionMethod.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsCompressionMethod.cs new file mode 100755 index 00000000000..371ad6ec72f --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsCompressionMethod.cs @@ -0,0 +1,37 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; + +namespace Mono.Security.Protocol.Tls +{ + // Information about compression methods allowed by TLS + // can be found in: + // draft-ietf-tls-compression-05.txt (http://www.ietf.org/internet-drafts/draft-ietf-tls-compression-05.txt) + public enum TlsCompressionMethod : byte + { + None = 0, + Zlib = 1 + } +} diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsNetworkStream.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsNetworkStream.cs new file mode 100644 index 00000000000..e30730eaf87 --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsNetworkStream.cs @@ -0,0 +1,369 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; + +namespace Mono.Security.Protocol.Tls +{ + public class TlsNetworkStream : Stream, IDisposable + { + #region PROPERTIES + + private TlsSocket socket; + private bool ownsSocket; + private bool canRead; + private bool canWrite; + private bool disposed; + + #endregion + + #region PROPERTIES + + public override bool CanRead + { + get { return canRead; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return canWrite; } + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + + } + + public bool DataAvailable + { + get + { + if (socket == null) + { + throw new IOException(); + } + + if (this.socket.Session.IsSecure) + { + if ((this.socket.InputBuffer.Length - this.socket.InputBuffer.Position) > 0) + { + return true; + } + } + + // If there are bytes in the socket buffer return true + // otherwise false + return this.socket.Available != 0; + } + } + + #endregion + + #region DESTRUCTOR + + ~TlsNetworkStream() + { + this.Dispose(false); + } + + #endregion + + #region IDISPOSABLE + + void IDisposable.Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + if (this.socket != null) + { + if (this.ownsSocket) + { + try + { + this.socket.Shutdown(SocketShutdown.Both); + } + catch{} + finally + { + this.socket.Close(); + } + } + } + this.ownsSocket = false; + this.canRead = false; + this.canWrite = false; + this.socket = null; + } + + disposed = true; + } + } + + #endregion + + #region PROTECTED_PROPERTIES + + protected bool Readable + { + get { return this.canRead; } + set { this.canRead = value;} + } + + protected TlsSocket Socket + { + get { return socket; } + } + + protected bool Writeable + { + get { return this.canWrite; } + set { this.canWrite = value; } + } + + #endregion + + #region CONSTRUCTORS + + public TlsNetworkStream(TlsSocket socket) + : this(socket, FileAccess.ReadWrite, false) + { + } + + public TlsNetworkStream(TlsSocket socket, bool ownsSocket) + : this(socket, FileAccess.ReadWrite, ownsSocket) + { + } + + public TlsNetworkStream(TlsSocket socket, FileAccess access) + : this(socket, FileAccess.ReadWrite, false) + { + } + + public TlsNetworkStream(TlsSocket socket, FileAccess access, bool ownsSocket) + { + if (socket == null) + { + throw new ArgumentNullException("socket is a null reference."); + } + if (!socket.Blocking) + { + throw new IOException("socket is in a nonblocking state."); + } + if (socket.SocketType != SocketType.Stream) + { + throw new IOException("The SocketType property of socket is not SocketType.Stream."); + } + if (!socket.Connected) + { + throw new IOException("socket is not connected."); + } + + this.socket = socket; + this.ownsSocket = ownsSocket; + switch (access) + { + case FileAccess.Read: + this.canRead = true; + break; + + case FileAccess.ReadWrite: + this.canRead = true; + this.canWrite = true; + break; + + case FileAccess.Write: + this.canWrite = true; + break; + } + } + + #endregion + + #region METHODS + + public override IAsyncResult BeginRead( + byte[] buffer, + int offset, + int count, + AsyncCallback callback, + object state) + { + throw new NotSupportedException(); + } + + public override IAsyncResult BeginWrite( + byte[] buffer, + int offset, + int count, + AsyncCallback callback, + object state) + { + throw new NotSupportedException(); + } + + public override int EndRead(IAsyncResult asyncResult) + { + throw new NotSupportedException(); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + throw new NotSupportedException(); + } + + public override void Close() + { + ((IDisposable)this).Dispose(); + } + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int size) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer is a null reference."); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException("offset is less than 0."); + } + if (offset > buffer.Length) + { + throw new ArgumentOutOfRangeException("offset is greater than the length of buffer."); + } + if (size < 0) + { + throw new ArgumentOutOfRangeException("size is less than 0."); + } + if (size > (buffer.Length - offset)) + { + throw new ArgumentOutOfRangeException("size is less than the length of buffer minus the value of the offset parameter."); + } + if (disposed) + { + throw new ObjectDisposedException("The NetworkStream is closed."); + } + if (!socket.Connected) + { + throw new IOException("The underlying Socket is closed."); + } + + try + { + return socket.Receive(buffer, offset, size, SocketFlags.None); + } + catch (TlsException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new IOException("IO exception during read.", ex); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int size) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer is a null reference."); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException("offset is less than 0."); + } + if (offset > buffer.Length) + { + throw new ArgumentOutOfRangeException("offset is greater than the length of buffer."); + } + if (size < 0) + { + throw new ArgumentOutOfRangeException("size is less than 0."); + } + if (size > (buffer.Length - offset)) + { + throw new ArgumentOutOfRangeException("size is less than the length of buffer minus the value of the offset parameter."); + } + if (disposed) + { + throw new ObjectDisposedException("The NetworkStream is closed."); + } + if (!socket.Connected) + { + throw new IOException("The underlying Socket is closed."); + } + + try + { + socket.Send(buffer, offset, size, SocketFlags.None); + } + catch (TlsException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new IOException("IO exception during Write.", ex); + } + } + + #endregion + } +} diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsProtocol.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsProtocol.cs new file mode 100644 index 00000000000..4315a1e2c5d --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsProtocol.cs @@ -0,0 +1,34 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; + +namespace Mono.Security.Protocol.Tls +{ + public enum TlsProtocol : short + { + Tls1 = (0x03 << 8) | 0x01, + Ssl3 = (0x03 << 8) | 0x00 + } +}
\ No newline at end of file diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSession.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSession.cs new file mode 100644 index 00000000000..1303c7c3b80 --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSession.cs @@ -0,0 +1,265 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.IO; +using System.Collections; +using System.Text; +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; + +using Mono.Security.Protocol.Tls.Alerts; + +namespace Mono.Security.Protocol.Tls +{ + public sealed class TlsSession + { + #region EVENTS + + public event TlsWarningAlertEventHandler WarningAlert; + + #endregion + + #region FIELDS + + private byte[] sessionId; + private TlsSessionContext context; + private TlsSessionSettings settings; + private TlsSocket socket; + private TlsNetworkStream networkStream; + private bool isSecure; + private TlsSessionState state; + + #endregion + + #region PROPERTIES + + public byte[] SessionId + { + get { return sessionId; } + } + + public TlsNetworkStream NetworkStream + { + get { return networkStream; } + } + + public TlsSessionState State + { + get { return state; } + } + + #endregion + + #region INTERNAL_PROPERTIES + + internal TlsSessionContext Context + { + get { return context; } + } + + internal bool IsSecure + { + get { return isSecure; } + set { isSecure = value; } + } + + internal TlsSessionSettings Settings + { + get { return settings; } + } + + #endregion + + #region CONSTRUCTORS + + public TlsSession(TlsSessionSettings settings) + { + this.settings = settings; + this.context = new TlsSessionContext(); + this.sessionId = new byte[0]; + + // Initialize socket for connection + this.initializeSocket(); + } + + #endregion + + #region EXCEPTION_METHODS + + internal TlsException CreateException(TlsAlertLevel alertLevel, TlsAlertDescription alertDesc) + { + return CreateException(TlsAlert.GetAlertMessage(alertDesc)); + } + + internal TlsException CreateException(string format, params object[] args) + { + StringBuilder message = new StringBuilder(); + message.AppendFormat(format, args); + + return CreateException(message.ToString()); + } + + internal TlsException CreateException(string message) + { + this.state = TlsSessionState.Broken; + + // Throw an exception will made the connection unavailable + // for this both streams will be closed + closeStreams(); + + return new TlsException(message); + } + + #endregion + + #region METHODS + + public void Open() + { + try + { + this.context.Protocol = settings.Protocol; + this.context.CompressionMethod = settings.CompressionMethod; + this.context.SupportedCiphers = TlsCipherSuiteFactory.GetSupportedCiphers(context.Protocol); + this.state = TlsSessionState.OpeningSecure; + this.socket.DoHandshake(); + this.state = TlsSessionState.OpenSecure; + } + catch (TlsException ex) + { + this.state = TlsSessionState.Broken; + throw ex; + } + catch (Exception ex) + { + this.state = TlsSessionState.Broken; + this.closeStreams(); + throw ex; + } + } + + public void Close() + { + try + { + this.state = TlsSessionState.Closing; + + if (isSecure) + { + TlsCloseNotifyAlert alert = new TlsCloseNotifyAlert(this); + + // Write close notify + this.socket.SendAlert(alert); + + // Check that the session is finished by the client and by server + if (!this.context.ConnectionEnd) + { + throw new TlsException("Invalid session termination"); + } + } + } + catch (Exception ex) + { + this.state = TlsSessionState.Broken; + throw ex; + } + finally + { + // Close streams + closeStreams(); + + this.state = TlsSessionState.Closed; + } + } + + #endregion + + #region INTERNAL_METHODS + + internal void RaiseWarningAlert(TlsAlertLevel level, TlsAlertDescription description) + { + if (WarningAlert != null) + { + WarningAlert(this, new TlsWarningAlertEventArgs(level, description)); + } + } + + internal void SetSessionId(byte[] sessionId) + { + this.sessionId = sessionId; + } + + #endregion + + #region PRIVATE_METHODS + + private void initializeSocket() + { + try + { + this.state = TlsSessionState.Opening; + + // Initialize socket + IPAddress hostadd = Dns.Resolve(settings.ServerName).AddressList[0]; + IPEndPoint EPhost = new IPEndPoint(hostadd, settings.ServerPort); + + // Create the socket + socket = new TlsSocket( + this, + AddressFamily.InterNetwork, + SocketType.Stream, + ProtocolType.IP); + + // Make the socket to connect to the Server + socket.Connect(EPhost); + networkStream = new TlsNetworkStream(socket, true); + + this.state = TlsSessionState.Open; + } + catch (Exception ex) + { + this.state = TlsSessionState.Broken; + throw ex; + } + } + + private void closeStreams() + { + // Reset session state + this.context.IsActual = false; + + // Close the socket and the networkStream + this.networkStream.Close(); + + // Reset session information + this.isSecure = false; + this.context = new TlsSessionContext(); + this.sessionId = new byte[0]; + } + + #endregion + } +}
\ No newline at end of file diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSessionContext.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSessionContext.cs new file mode 100644 index 00000000000..c9e85cee0d2 --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSessionContext.cs @@ -0,0 +1,289 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Text; +using System.Security.Cryptography; + +using Mono.Security.Cryptography; +using Mono.Security.Protocol.Tls.Handshake; + +namespace Mono.Security.Protocol.Tls +{ + internal class TlsSessionContext + { + #region FIELDS + + // Protocol version + private TlsProtocol protocol; + + // Compression method + private TlsCompressionMethod compressionMethod; + + // Information sent and request by the server in the Handshake protocol + private TlsServerSettings serverSettings; + + // Cipher suite information + private CipherSuite cipher; + private TlsCipherSuiteCollection supportedCiphers; + + // Misc + private bool isActual; + private bool helloDone; + private bool handshakeFinished; + private bool connectionEnd; + + // Sequence numbers + private long writeSequenceNumber; + private long readSequenceNumber; + + // Random data + private byte[] clientRandom; + private byte[] serverRandom; + private byte[] randomCS; + private byte[] randomSC; + + // Key information + private byte[] masterSecret; + private byte[] clientWriteMAC; + private byte[] serverWriteMAC; + private byte[] clientWriteKey; + private byte[] serverWriteKey; + private byte[] clientWriteIV; + private byte[] serverWriteIV; + + // Handshake hashes + private TlsStream handshakeMessages; + + #endregion + + #region INTERNAL_CONSTANTS + + internal const short MAX_FRAGMENT_SIZE = 16384; // 2^14 + + #endregion + + #region PROPERTIES + + public TlsProtocol Protocol + { + get { return this.protocol; } + set { this.protocol = value; } + } + + public TlsCompressionMethod CompressionMethod + { + get { return this.compressionMethod; } + set { this.compressionMethod = value; } + } + + public TlsServerSettings ServerSettings + { + get { return this.serverSettings; } + set { this.serverSettings = value; } + } + + public bool IsActual + { + get { return this.isActual; } + set { this.isActual = value; } + } + + public bool HelloDone + { + get { return helloDone; } + set { helloDone = value; } + } + + public bool HandshakeFinished + { + get { return handshakeFinished; } + set { handshakeFinished = value; } + } + + public bool ConnectionEnd + { + get { return this.connectionEnd; } + set { this.connectionEnd = value; } + } + + public CipherSuite Cipher + { + get { return this.cipher; } + set { this.cipher = value; } + } + + public TlsCipherSuiteCollection SupportedCiphers + { + get { return supportedCiphers; } + set { supportedCiphers = value; } + } + + public TlsStream HandshakeMessages + { + get { return this.handshakeMessages; } + } + + public long WriteSequenceNumber + { + get { return this.writeSequenceNumber; } + set { this.writeSequenceNumber = value; } + } + + public long ReadSequenceNumber + { + get { return this.readSequenceNumber; } + set { this.readSequenceNumber = value; } + } + + public byte[] ClientRandom + { + get { return this.clientRandom; } + set { this.clientRandom = value; } + } + + public byte[] ServerRandom + { + get { return this.serverRandom; } + set { this.serverRandom = value; } + } + + public byte[] RandomCS + { + get { return this.randomCS; } + set { this.randomCS = value; } + } + + public byte[] RandomSC + { + get { return this.randomSC; } + set { this.randomSC = value; } + } + + public byte[] MasterSecret + { + get { return this.masterSecret; } + set { this.masterSecret = value; } + } + + public byte[] ClientWriteMAC + { + get { return this.clientWriteMAC; } + set { this.clientWriteMAC = value; } + } + + public byte[] ServerWriteMAC + { + get { return this.serverWriteMAC; } + set { this.serverWriteMAC = value; } + } + + public byte[] ClientWriteKey + { + get { return this.clientWriteKey; } + set { this.clientWriteKey = value; } + } + + public byte[] ServerWriteKey + { + get { return this.serverWriteKey; } + set { this.serverWriteKey = value; } + } + + public byte[] ClientWriteIV + { + get { return this.clientWriteIV; } + set { this.clientWriteIV = value; } + } + + public byte[] ServerWriteIV + { + get { return this.serverWriteIV; } + set { this.serverWriteIV = value; } + } + + #endregion + + #region CONSTRUCTORS + + public TlsSessionContext() + { + this.protocol = TlsProtocol.Tls1; + this.compressionMethod = TlsCompressionMethod.None; + this.serverSettings = new TlsServerSettings(); + this.handshakeMessages = new TlsStream(); + } + + #endregion + + #region METHODS + + public int GetUnixTime() + { + DateTime now = DateTime.Now.ToUniversalTime(); + TimeSpan unixTime = now.Subtract(new DateTime(1970, 1, 1)); + + return (int)unixTime.TotalSeconds; + } + + public byte[] GetSecureRandomBytes(int count) + { + byte[] secureBytes = new byte[count]; + + RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); + rng.GetNonZeroBytes(secureBytes); + + return secureBytes; + } + + public void ClearKeyInfo() + { + // Clear Master Secret + this.masterSecret = null; + + // Clear client and server random + this.clientRandom = null; + this.serverRandom = null; + this.randomCS = null; + this.randomSC = null; + + // Clear client keys + this.clientWriteKey = null; + this.clientWriteIV = null; + + // Clear server keys + this.serverWriteKey = null; + this.serverWriteIV = null; + + // Clear MAC keys if protocol is different than Ssl3 + if (this.protocol != TlsProtocol.Ssl3) + { + this.clientWriteMAC = null; + this.serverWriteMAC = null; + } + } + + #endregion + } +} diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSessionSettings.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSessionSettings.cs new file mode 100644 index 00000000000..7614e99f207 --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSessionSettings.cs @@ -0,0 +1,218 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Text; +using System.Security.Cryptography.X509Certificates; + +namespace Mono.Security.Protocol.Tls +{ + public sealed class TlsSessionSettings + { + #region FIELDS + + private string serverName; + private int serverPort; + private Encoding encoding; + private TlsProtocol protocol; + private TlsCompressionMethod compressionMethod; + private X509CertificateCollection certificates; + + #endregion + + #region PROPERTIES + + public string ServerName + { + get { return serverName; } + set { serverName = value; } + } + + public int ServerPort + { + get { return serverPort; } + set { serverPort = value; } + } + + public Encoding Encoding + { + get { return encoding; } + set { encoding = value; } + } + + public TlsProtocol Protocol + { + get { return protocol; } + set + { + if (value != TlsProtocol.Tls1 && + value != TlsProtocol.Ssl3) + { + throw new NotSupportedException("Specified protocol is not supported"); + } + protocol = value; + } + } + + public TlsCompressionMethod CompressionMethod + { + get { return compressionMethod; } + set + { + if (value != TlsCompressionMethod.None) + { + throw new NotSupportedException("Specified compression method is not supported"); + } + compressionMethod = value; + } + } + + public X509CertificateCollection Certificates + { + get { return certificates; } + set { certificates = value; } + } + + #endregion + + #region CONSTRUCTORS + + public TlsSessionSettings() + { + this.protocol = TlsProtocol.Tls1; + this.compressionMethod = TlsCompressionMethod.None; + this.certificates = new X509CertificateCollection(); + this.serverName = "localhost"; + this.serverPort = 443; + this.encoding = Encoding.Default; + } + + public TlsSessionSettings(TlsProtocol protocol) : this() + { + this.Protocol = protocol; + } + + public TlsSessionSettings(TlsProtocol protocol, Encoding encoding) : this(protocol) + { + this.encoding = encoding; + } + + public TlsSessionSettings(string serverName) : this() + { + this.serverName = serverName; + } + + public TlsSessionSettings(string serverName, Encoding encoding) : this() + { + this.serverName = serverName; + this.encoding = encoding; + } + + public TlsSessionSettings(string serverName, int serverPort) : this() + { + this.serverName = serverName; + this.serverPort = serverPort; + } + + public TlsSessionSettings(string serverName, int serverPort, Encoding encoding) : this() + { + this.serverName = serverName; + this.serverPort = serverPort; + this.encoding = encoding; + } + + public TlsSessionSettings(TlsProtocol protocol, string serverName) : this(protocol) + { + this.serverName = serverName; + } + + public TlsSessionSettings(TlsProtocol protocol, string serverName, Encoding encoding) : this(protocol) + { + this.serverName = serverName; + this.encoding = encoding; + } + + + public TlsSessionSettings(TlsProtocol protocol, string serverName, int serverPort) : this(protocol) + { + this.serverName = serverName; + this.serverPort = serverPort; + } + + public TlsSessionSettings(TlsProtocol protocol, string serverName, int serverPort, Encoding encoding) : this(protocol) + { + this.serverName = serverName; + this.serverPort = serverPort; + this.encoding = encoding; + } + + public TlsSessionSettings(TlsProtocol protocol, X509CertificateCollection certificates) : this(protocol) + { + this.certificates = certificates; + } + + public TlsSessionSettings(TlsProtocol protocol, X509CertificateCollection certificates, Encoding encoding) : this(protocol) + { + this.certificates = certificates; + this.encoding = encoding; + } + + public TlsSessionSettings(TlsProtocol protocol, X509CertificateCollection certificates, string serverName, int serverPort) : this(protocol) + { + this.certificates = certificates; + this.serverName = serverName; + this.serverPort = serverPort; + } + + public TlsSessionSettings(TlsProtocol protocol, X509CertificateCollection certificates, string serverName, int serverPort, Encoding encoding) : this(protocol) + { + this.certificates = certificates; + this.serverName = serverName; + this.serverPort = serverPort; + this.encoding = encoding; + } + + public TlsSessionSettings(TlsProtocol protocol, X509Certificate[] certificates) + : this(protocol, new X509CertificateCollection(certificates)) + { + } + + public TlsSessionSettings(TlsProtocol protocol, X509Certificate[] certificates, Encoding encoding) + : this(protocol, new X509CertificateCollection(certificates), encoding) + { + } + + public TlsSessionSettings(TlsProtocol protocol, X509Certificate[] certificates, string serverName, int serverPort) : + this(protocol, new X509CertificateCollection(certificates), serverName, serverPort) + { + } + + public TlsSessionSettings(TlsProtocol protocol, X509Certificate[] certificates, string serverName, int serverPort, Encoding encoding) : + this(protocol, new X509CertificateCollection(certificates), serverName, serverPort, encoding) + { + } + + #endregion + } +} diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSocket.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSocket.cs new file mode 100644 index 00000000000..dce8206a683 --- /dev/null +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/TlsSocket.cs @@ -0,0 +1,664 @@ +/* Transport Security Layer (TLS) + * Copyright (c) 2003 Carlos Guzmán Álvarez + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.IO; +using System.Net; +using System.Collections; +using System.Net.Sockets; +using System.Security.Cryptography; + +using Mono.Security.Protocol.Tls; +using Mono.Security.Protocol.Tls.Alerts; +using Mono.Security.Protocol.Tls.Handshake; +using Mono.Security.Protocol.Tls.Handshake.Client; + +namespace Mono.Security.Protocol.Tls +{ + public sealed class TlsSocket : Socket + { + #region FIELDS + + private TlsSession session; + private BufferedStream inputBuffer; + + #endregion + + #region PROPERTIES + + internal TlsSession Session + { + get { return this.session; } + } + + internal BufferedStream InputBuffer + { + get { return this.inputBuffer; } + } + + #endregion + + #region CONSTRUCTORS + + private TlsSocket( + AddressFamily addressFamily, + SocketType socketType, + ProtocolType protocolType + ) : base(addressFamily, socketType, protocolType) + { + this.inputBuffer = new BufferedStream(new MemoryStream()); + } + + public TlsSocket( + TlsSession session, + AddressFamily addressFamily, + SocketType socketType, + ProtocolType protocolType + ) : this(addressFamily, socketType, protocolType) + { + this.session = session; + } + + #endregion + + #region REPLACED_METHODS + + public new void Close() + { + this.resetBuffer(); + base.Close(); + if (this.session.State != TlsSessionState.Closing && + this.session.State != TlsSessionState.Closed) + { + this.session.Close(); + } + } + + public new int Receive(byte[] buffer) + { + return this.Receive(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None); + } + + public new int Receive(byte[] buffer, SocketFlags socketFlags) + { + return this.Receive(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags); + } + + public new int Receive(byte[] buffer, int size, SocketFlags socketFlags) + { + return this.Receive(buffer, 0, size, socketFlags); + } + + public new int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags) + { + if (!this.session.IsSecure) + { + return base.Receive(buffer, offset, size, socketFlags); + } + + // If actual buffer is full readed reset it + if (this.inputBuffer.Position == this.inputBuffer.Length) + { + this.resetBuffer(); + } + + // Check if we have space in the middle buffer + // if not Read next TLS record and update the inputBuffer + while ((this.inputBuffer.Length - this.inputBuffer.Position) < size) + { + // Read next record and write it into the inputBuffer + long position = this.inputBuffer.Position; + byte[] record = this.receiveRecord(); + + if (record.Length > 0) + { + // Write new data to the inputBuffer + this.inputBuffer.Seek(0, SeekOrigin.End); + this.inputBuffer.Write(record, 0, record.Length); + + // Restore buffer position + this.inputBuffer.Seek(position, SeekOrigin.Begin); + } + + if (base.Available == 0) + { + break; + } + } + + return this.inputBuffer.Read(buffer, offset, size); + } + + public new int Send(byte[] buffer) + { + return this.Send(buffer, 0, buffer != null ? buffer.Length : 0, SocketFlags.None); + } + + public new int Send(byte[] buffer, SocketFlags socketFlags) + { + return this.Send(buffer, 0, buffer != null ? buffer.Length : 0, socketFlags); + } + + public new int Send(byte[] buffer, int size, SocketFlags socketFlags) + { + return this.Send(buffer, 0, size, socketFlags); + } + + public new int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags) + { + if (!this.session.IsSecure) + { + return base.Send(buffer, offset, size, socketFlags); + } + + // Send the buffer as a TLS record + byte[] recordData = new byte[size]; + System.Array.Copy(buffer, offset, recordData, 0, size); + + return this.sendRecord(TlsContentType.ApplicationData, recordData); + } + + #endregion + + #region TLS_RECORD_METHODS + + private byte[] receiveRecord() + { + if (this.session.Context.ConnectionEnd) + { + throw this.session.CreateException("The session is finished and it's no longer valid."); + } + + TlsContentType contentType = (TlsContentType)this.ReadByte(); + TlsProtocol protocol = (TlsProtocol)this.ReadShort(); + short length = this.ReadShort(); + + // Read Record data + int received = 0; + byte[] buffer = new byte[length]; + while (received != length) + { + received += base.Receive( + buffer, received, buffer.Length - received, SocketFlags.None); + } + + TlsStream message = new TlsStream(buffer); + + // Check that the message has a valid protocol version + if (protocol != this.session.Context.Protocol) + { + throw session.CreateException("Invalid protocol version on message received from server"); + } + + // Decrypt message contents if needed + if (contentType == TlsContentType.Alert && length == 2) + { + } + else + { + if (session.Context.IsActual && + contentType != TlsContentType.ChangeCipherSpec) + { + message = this.decryptRecordFragment( + contentType, + protocol, + message.ToArray()); + } + } + + byte[] result = message.ToArray(); + + // Process record + switch (contentType) + { + case TlsContentType.Alert: + this.processAlert((TlsAlertLevel)message.ReadByte(), + (TlsAlertDescription)message.ReadByte()); + break; + + case TlsContentType.ChangeCipherSpec: + // Reset sequence numbers + this.session.Context.ReadSequenceNumber = 0; + break; + + case TlsContentType.ApplicationData: + break; + + case TlsContentType.Handshake: + while (!message.EOF) + { + this.processHandshakeMessage(message); + } + // Update handshakes of current messages + this.session.Context.HandshakeMessages.Write(message.ToArray()); + break; + + default: + throw session.CreateException("Unknown record received from server."); + } + + return result; + } + + #endregion + + #region TLS_CRYPTO_METHODS + + private byte[] encryptRecordFragment(TlsContentType contentType, byte[] fragment) + { + // Calculate message MAC + byte[] mac = this.session.Context.Cipher.ComputeClientRecordMAC(contentType, fragment); + + // Encrypt the message + byte[] ecr = this.session.Context.Cipher.EncryptRecord(fragment, mac); + + // Set new IV + if (this.session.Context.Cipher.CipherMode == CipherMode.CBC) + { + byte[] iv = new byte[this.session.Context.Cipher.IvSize]; + System.Array.Copy(ecr, ecr.Length - iv.Length, iv, 0, iv.Length); + this.session.Context.Cipher.UpdateClientCipherIV(iv); + } + + // Update sequence number + this.session.Context.WriteSequenceNumber++; + + return ecr; + } + + private TlsStream decryptRecordFragment(TlsContentType contentType, + TlsProtocol protocol, + byte[] fragment) + { + byte[] dcrFragment = null; + byte[] dcrMAC = null; + + // Decrypt message + this.session.Context.Cipher.DecryptRecord(fragment, ref dcrFragment, ref dcrMAC); + + // Set new IV + if (this.session.Context.Cipher.CipherMode == CipherMode.CBC) + { + byte[] iv = new byte[session.Context.Cipher.IvSize]; + System.Array.Copy(fragment, fragment.Length - iv.Length, iv, 0, iv.Length); + this.session.Context.Cipher.UpdateServerCipherIV(iv); + } + + // Check MAC code + byte[] mac = this.session.Context.Cipher.ComputeServerRecordMAC(contentType, dcrFragment); + + // Check that the mac is correct + if (mac.Length != dcrMAC.Length) + { + throw new TlsException("Invalid MAC received from server."); + } + for (int i = 0; i < mac.Length; i++) + { + if (mac[i] != dcrMAC[i]) + { + throw new TlsException("Invalid MAC received from server."); + } + } + + // Update sequence number + this.session.Context.ReadSequenceNumber++; + + return new TlsStream(dcrFragment); + } + + #endregion + + #region TLS_SEND_METHODS + + internal int SendAlert(TlsAlert alert) + { + // Write record + int bytesSent = this.sendRecord(TlsContentType.Alert, alert.ToArray()); + + // Update session + alert.UpdateSession(); + + // Reset message contents + alert.Reset(); + + return bytesSent; + } + + private int sendRecord(TlsHandshakeType type) + { + TlsHandshakeMessage msg = createClientHandshakeMessage(type); + + // Write record + int bytesSent = this.sendRecord(msg.ContentType, msg.EncodeMessage()); + + // Update session + msg.UpdateSession(); + + // Reset message contents + msg.Reset(); + + return bytesSent; + } + + private int sendChangeCipherSpec() + { + // Send Change Cipher Spec message + int bytesSent = this.sendRecord(TlsContentType.ChangeCipherSpec, new byte[] {1}); + + // Reset sequence numbers + this.session.Context.WriteSequenceNumber = 0; + + // Make the pending state to be the current state + this.session.Context.IsActual = true; + + // Send Finished message + bytesSent += this.sendRecord(TlsHandshakeType.Finished); + + return bytesSent; + } + + private int sendRecord(TlsContentType contentType, byte[] recordData) + { + if (this.session.Context.ConnectionEnd) + { + throw this.session.CreateException("The session is finished and it's no longer valid."); + } + + int bytesSent = 0; + byte[][] fragments = fragmentData(recordData); + for (int i = 0; i < fragments.Length; i++) + { + byte[] fragment = fragments[i]; + + if (this.session.Context.IsActual) + { + // Encrypt fragment + fragment = this.encryptRecordFragment(contentType, fragment); + } + + // Write tls message + TlsStream record = new TlsStream(); + record.Write((byte)contentType); + record.Write((short)this.session.Context.Protocol); + record.Write((short)fragment.Length); + record.Write(fragment); + + // Write record + bytesSent += base.Send(record.ToArray()); + + // Reset record data + record.Reset(); + } + + return bytesSent; + } + + private byte[][] fragmentData(byte[] messageData) + { + ArrayList d = new ArrayList(); + + int position = 0; + + while (position < messageData.Length) + { + short fragmentLength = 0; + byte[] fragmentData; + if ((messageData.Length - position) > TlsSessionContext.MAX_FRAGMENT_SIZE) + { + fragmentLength = TlsSessionContext.MAX_FRAGMENT_SIZE; + } + else + { + fragmentLength = (short)(messageData.Length - position); + } + fragmentData = new byte[fragmentLength]; + + System.Array.Copy(messageData, position, fragmentData, 0, fragmentLength); + + d.Add(fragmentData); + + position += fragmentLength; + } + + byte[][] result = new byte[d.Count][]; + for (int i = 0; i < d.Count; i++) + { + result[i] = (byte[])d[i]; + } + + return result; + } + + #endregion + + #region MESSAGE_PROCESSING + + private void processHandshakeMessage(TlsStream handMsg) + { + TlsHandshakeType handshakeType = (TlsHandshakeType)handMsg.ReadByte(); + TlsHandshakeMessage message = null; + + // Read message length + int length = handMsg.ReadInt24(); + + // Read message data + byte[] data = new byte[length]; + handMsg.Read(data, 0, length); + + // Create and process the server message + message = this.createServerHandshakeMessage(handshakeType, data); + + // Update session + if (message != null) + { + message.UpdateSession(); + } + } + + private void processAlert(TlsAlertLevel alertLevel, TlsAlertDescription alertDesc) + { + switch (alertLevel) + { + case TlsAlertLevel.Fatal: + throw this.session.CreateException(alertLevel, alertDesc); + + case TlsAlertLevel.Warning: + default: + switch (alertDesc) + { + case TlsAlertDescription.CloseNotify: + this.session.Context.ConnectionEnd = true; + break; + + default: + this.session.RaiseWarningAlert(alertLevel, alertDesc); + break; + } + break; + } + } + + #endregion + + #region MISC_METHODS + + private void resetBuffer() + { + this.inputBuffer.SetLength(0); + this.inputBuffer.Position = 0; + } + + private byte ReadByte() + { + byte[] b = new byte[1]; + base.Receive(b); + + return b[0]; + } + + private short ReadShort() + { + byte[] b = new byte[2]; + base.Receive(b); + + short val = BitConverter.ToInt16(b, 0); + + return System.Net.IPAddress.HostToNetworkOrder(val); + } + + #endregion + + #region HANDSHAKE_METHODS + + /* + Client Server + + ClientHello --------> + ServerHello + Certificate* + ServerKeyExchange* + CertificateRequest* + <-------- ServerHelloDone + Certificate* + ClientKeyExchange + CertificateVerify* + [ChangeCipherSpec] + Finished --------> + [ChangeCipherSpec] + <-------- Finished + Application Data <-------> Application Data + + Fig. 1 - Message flow for a full handshake + */ + + internal void DoHandshake() + { + // Reset isSecure field + this.session.IsSecure = false; + + // Send client hello + this.sendRecord(TlsHandshakeType.ClientHello); + + // Read server response + while (!this.session.Context.HelloDone) + { + // Read next record + this.receiveRecord(); + } + + // Send client certificate if requested + if (this.session.Context.ServerSettings.CertificateRequest) + { + this.sendRecord(TlsHandshakeType.Certificate); + } + + // Send Client Key Exchange + this.sendRecord(TlsHandshakeType.ClientKeyExchange); + + // Now initialize session cipher with the generated keys + this.session.Context.Cipher.InitializeCipher(); + + // Send certificate verify if requested + if (this.session.Context.ServerSettings.CertificateRequest) + { + this.sendRecord(TlsHandshakeType.CertificateVerify); + } + + // Send Cipher Spec protocol + this.sendChangeCipherSpec(); + + // Read Cipher Spec protocol + this.receiveRecord(); + + // Read server finished + if (!this.session.Context.HandshakeFinished) + { + this.receiveRecord(); + } + + // Clear Key Info + this.session.Context.ClearKeyInfo(); + + // Set isSecure + this.session.IsSecure = true; + } + + private TlsHandshakeMessage createClientHandshakeMessage(TlsHandshakeType type) + { + switch (type) + { + case TlsHandshakeType.ClientHello: + return new TlsClientHello(session); + + case TlsHandshakeType.Certificate: + return new TlsClientCertificate(session); + + case TlsHandshakeType.ClientKeyExchange: + return new TlsClientKeyExchange(session); + + case TlsHandshakeType.CertificateVerify: + return new TlsClientCertificateVerify(session); + + case TlsHandshakeType.Finished: + return new TlsClientFinished(session); + + default: + throw new InvalidOperationException("Unknown client handshake message type: " + type.ToString() ); + } + } + + private TlsHandshakeMessage createServerHandshakeMessage(TlsHandshakeType type, byte[] buffer) + { + switch (type) + { + case TlsHandshakeType.HelloRequest: + this.sendRecord(TlsHandshakeType.ClientHello); + return null; + + case TlsHandshakeType.ServerHello: + return new TlsServerHello(session, buffer); + + case TlsHandshakeType.Certificate: + return new TlsServerCertificate(session, buffer); + + case TlsHandshakeType.ServerKeyExchange: + return new TlsServerKeyExchange(session, buffer); + + case TlsHandshakeType.CertificateRequest: + return new TlsServerCertificateRequest(session, buffer); + + case TlsHandshakeType.ServerHelloDone: + return new TlsServerHelloDone(session, buffer); + + case TlsHandshakeType.Finished: + return new TlsServerFinished(session, buffer); + + default: + throw this.session.CreateException("Unknown server handshake message received ({0})", type.ToString()); + } + } + + #endregion + } +}
\ No newline at end of file diff --git a/mcs/class/System.Data.ObjectSpaces/System.Data.ObjectSpaces/PersistenceErrorBehaviour.cs b/mcs/class/System.Data.ObjectSpaces/System.Data.ObjectSpaces/PersistenceErrorBehaviour.cs new file mode 100755 index 00000000000..7f24e7c36f4 --- /dev/null +++ b/mcs/class/System.Data.ObjectSpaces/System.Data.ObjectSpaces/PersistenceErrorBehaviour.cs @@ -0,0 +1,21 @@ +//
+// System.Data.ObjectSpaces.PersistenceErrorBehaviour.cs - The behaviour to follow when a persistence error occurs
+//
+// Author:
+// Mark Easton (mark.easton@blinksoftware.co.uk)
+//
+// (C) BLiNK Software Ltd. http://www.blinksoftware.co.uk
+//
+
+#if NET_1_2
+
+namespace System.Data.ObjectSpaces
+{
+ public enum PersistenceErrorBehaviour
+ {
+ ThrowAtFirstError,
+ ThrowAfterCompletion
+ }
+}
+
+#endif
\ No newline at end of file diff --git a/mcs/class/System.Data.ObjectSpaces/System.Data.ObjectSpaces/PersisteneceErrorType.cs b/mcs/class/System.Data.ObjectSpaces/System.Data.ObjectSpaces/PersisteneceErrorType.cs new file mode 100755 index 00000000000..d87e7a5bc0a --- /dev/null +++ b/mcs/class/System.Data.ObjectSpaces/System.Data.ObjectSpaces/PersisteneceErrorType.cs @@ -0,0 +1,23 @@ +//
+// System.Data.ObjectSpaces.PersistenceErrorType.cs - The type of persistence error
+//
+// Author:
+// Mark Easton (mark.easton@blinksoftware.co.uk)
+//
+// (C) BLiNK Software Ltd. http://www.blinksoftware.co.uk
+//
+
+#if NET_1_2
+
+namespace System.Data.ObjectSpaces
+{
+ public enum PersistenceErrorType
+ {
+ Inserting,
+ Deleting,
+ Updating,
+ Unknown
+ }
+}
+
+#endif
\ No newline at end of file diff --git a/mcs/class/System.Web/System.Web.UI/TODO b/mcs/class/System.Web/System.Web.UI/TODO new file mode 100644 index 00000000000..de529b87f86 --- /dev/null +++ b/mcs/class/System.Web/System.Web.UI/TODO @@ -0,0 +1,42 @@ +AttributeCollection +BaseParser +BasePartialCachingControl +CompiledTemplateBuilder +ConstructorNeedsTagAttribute +ControlBuilder +ControlBuilderAttribute +ControlCollection +CssStyleCollection +DataBinder +DataBinding +DataBindingCollection +DataBindingHandlerAttribute +DataBoundLiteralControl +DesignTimeParseData +DesignTimeTemplateParser +EmptyControlCollection +Html32TextWriter +HtmlTextWriter +ImageClickEventArgs +LosFormatter +Page +PageParser +ParseChildrenAttribute +PartialCachingAttribute +PartialCachingControl +PersistChildrenAttribute +PersistanceModeAttribute +RootBuilder +SimpleWebHandlerParser +StateBag +StaticPartialCachingControl +TagPrefixAttribute +TemplateBuilder +TemplateContainerAttribute +TemplateControl +TemplateControlParser +ToolboxDataAttribute +UserControl +ValidationPropertyAttribute +ValidatorCollection +WebServiceProvider diff --git a/mcs/class/System/Test/system_linux_test.args b/mcs/class/System/Test/system_linux_test.args new file mode 100644 index 00000000000..48b00f0b2dd --- /dev/null +++ b/mcs/class/System/Test/system_linux_test.args @@ -0,0 +1,37 @@ +Microsoft.CSharp/CodeGeneratorFromCompileUnitTest.cs +Microsoft.CSharp/CodeGeneratorFromExpressionTest.cs +Microsoft.CSharp/CodeGeneratorFromNamespaceTest.cs +Microsoft.CSharp/CodeGeneratorFromStatementTest.cs +Microsoft.CSharp/CodeGeneratorFromTypeTest.cs +Microsoft.CSharp/CodeGeneratorTestBase.cs +System/UriBuilderTest.cs +System/UriTest.cs +System.Collections.Specialized/BasicOperationsTest.cs +System.Collections.Specialized/BitVector32Test.cs +System.Collections.Specialized/HybridDictionaryTest.cs +System.Collections.Specialized/ListDictionaryTest.cs +System.Collections.Specialized/NameValueCollectionTest.cs +System.Collections.Specialized/StringCollectionTest.cs +System.ComponentModel/EventHandlerListTests.cs +System.Diagnostics/TraceTest.cs +System.Diagnostics/SwitchesTest.cs +System.Diagnostics/DiagnosticsConfigurationHandlerTest.cs +System.Net/CookieCollectionTest.cs +System.Net/CookieTest.cs +System.Net/CredentialCacheTest.cs +System.Net/DnsTest.cs +System.Net/FileWebRequestTest.cs +System.Net/HttpWebRequestTest.cs +System.Net/IPAddressTest.cs +System.Net/IPEndPointTest.cs +System.Net/ServicePointManagerTest.cs +System.Net/ServicePointTest.cs +System.Net/SocketPermissionTest.cs +System.Net/WebHeaderCollectionTest.cs +System.Net/WebProxyTest.cs +System.Net/WebRequestTest.cs +System.Net.Sockets/TcpClientTest.cs +System.Net.Sockets/TcpListenerTest.cs +System.Text.RegularExpressions/PerlTest.cs +System.Text.RegularExpressions/PerlTrials.cs +System.Text.RegularExpressions/RegexTrial.cs diff --git a/mcs/class/corlib/System.Reflection/common.src b/mcs/class/corlib/System.Reflection/common.src deleted file mode 100644 index 022b6768fcc..00000000000 --- a/mcs/class/corlib/System.Reflection/common.src +++ /dev/null @@ -1,25 +0,0 @@ -Assembly.cs -AssemblyNameFlags.cs -BindingFlags.cs -CallingConventions.cs -ConstructorInfo.cs -DefaultMemberAttribute.cs -EventAttributes.cs -EventInfo.cs -FieldAttributes.cs -FieldInfo.cs -ICustomAttributeProvider.cs -MemberFilter.cs -MemberInfo.cs -MemberTypes.cs -MethodAttributes.cs -MethodBase.cs -MethodImplAttributes.cs -MethodInfo.cs -Module.cs -ParameterAttributes.cs -PropertyAttributes.cs -PropertyInfo.cs -ResourceAttributes.cs -ResourceLocation.cs -TypeAttributes.cs diff --git a/mono/metadata/ChangeLog b/mono/metadata/ChangeLog index 163c2c269ac..05192716e37 100644 --- a/mono/metadata/ChangeLog +++ b/mono/metadata/ChangeLog @@ -1,3 +1,7 @@ +2003-11-30 Zoltan Varga <vargaz@freemail.hu> + + * reflection.c (fixup_method): Add support for MonoCMethod. + 2003-11-28 Zoltan Varga <vargaz@freemail.hu> * gc.c: Fix hangs and error messages when GC_DONT_GC is set. diff --git a/mono/metadata/reflection.c b/mono/metadata/reflection.c index b0109420c51..2c965ccab32 100644 --- a/mono/metadata/reflection.c +++ b/mono/metadata/reflection.c @@ -2990,7 +2990,8 @@ fixup_method (MonoReflectionILGen *ilgen, gpointer value, MonoDynamicImage *asse } else if (!strcmp (iltoken->member->vtable->klass->name, "ConstructorBuilder")) { ctor = (MonoReflectionCtorBuilder *)iltoken->member; idx = ctor->table_idx; - } else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod")) { + } else if (!strcmp (iltoken->member->vtable->klass->name, "MonoMethod") || + !strcmp (iltoken->member->vtable->klass->name, "MonoCMethod")) { MonoMethod *m = ((MonoReflectionMethod*)iltoken->member)->method; idx = GPOINTER_TO_UINT (mono_g_hash_table_lookup (assembly->method_to_table_idx, m)); } else { diff --git a/mono/mini/cpu-g4.md b/mono/mini/cpu-g4.md index 9ac80f0d45c..034a4126c1c 100644 --- a/mono/mini/cpu-g4.md +++ b/mono/mini/cpu-g4.md @@ -229,7 +229,8 @@ mul.ovf: dest:i src1:i src2:i len:8 mul.ovf.un: dest:i src1:i src2:i len:12 sub.ovf: sub.ovf.un: -endfinally: len:10 +start_handler: len:8 +endfinally: len:12 leave: leave.s: stind.i: @@ -257,7 +258,7 @@ ldloc: ldloca: stloc: localloc: dest:i src1:i len:30 -endfilter: +endfilter: len:12 unaligned.: volatile.: tail.: diff --git a/mono/mini/inssel-ppc.brg b/mono/mini/inssel-ppc.brg index 5e46e4c7bdc..85447734ee0 100644 --- a/mono/mini/inssel-ppc.brg +++ b/mono/mini/inssel-ppc.brg @@ -12,7 +12,23 @@ stmt: OP_START_HANDLER { MonoInst *spvar = mono_find_spvar_for_region (s, s->cbb->region); - MONO_EMIT_NEW_STORE_MEMBASE (s, OP_STORE_MEMBASE_REG, spvar->inst_basereg, spvar->inst_offset, ppc_sp); + /*MONO_EMIT_NEW_STORE_MEMBASE (s, OP_STORE_MEMBASE_REG, spvar->inst_basereg, spvar->inst_offset, ppc_sp); + */ + tree->inst_left = spvar; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: CEE_ENDFINALLY { + MonoInst *spvar = mono_find_spvar_for_region (s, s->cbb->region); + tree->inst_left = spvar; + mono_bblock_add_inst (s->cbb, tree); +} + +stmt: OP_ENDFILTER (reg) { + MonoInst *spvar = mono_find_spvar_for_region (s, s->cbb->region); + tree->inst_left = spvar; + tree->sreg1 = state->left->reg1; + mono_bblock_add_inst (s->cbb, tree); } stmt: CEE_STIND_I8 (OP_REGVAR, lreg) { diff --git a/mono/mini/mini-ppc.c b/mono/mini/mini-ppc.c index ae6fa0647dc..9414cd61c91 100644 --- a/mono/mini/mini-ppc.c +++ b/mono/mini/mini-ppc.c @@ -1685,8 +1685,8 @@ alloc_int_reg (MonoCompile *cfg, InstList *curinst, MonoInst *ins, int sym_reg, } /* use ppc_r3-ppc_10 as temp registers */ -#define PPC_CALLER_REGS (0x7f<<3) -#define PPC_CALLER_FREGS (0x7f<<2) +#define PPC_CALLER_REGS (0xff<<3) +#define PPC_CALLER_FREGS (0xff<<2) /* * Local register allocation. @@ -2305,7 +2305,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) ppc_rlwinm (code, ins->dreg, ins->sreg1, 0, 16, 31); break; case OP_COMPARE: - if (ins->next && ins->next->opcode >= CEE_BNE_UN && ins->next->opcode <= CEE_BLT_UN) + if (ins->next && (ins->next->opcode >= CEE_BNE_UN && ins->next->opcode <= CEE_BLT_UN)) ppc_cmpl (code, 0, 0, ins->sreg1, ins->sreg2); else ppc_cmp (code, 0, 0, ins->sreg1, ins->sreg2); @@ -2365,8 +2365,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; case OP_SUB_IMM: // we add the negated value - g_assert (ppc_is_imm16 (-ins->inst_imm)); - ppc_addi (code, ins->dreg, ins->sreg1, -ins->inst_imm); + if (ppc_is_imm16 (-ins->inst_imm)) + ppc_addi (code, ins->dreg, ins->sreg1, -ins->inst_imm); + else + ppc_load (code, ppc_r11, ins->inst_imm); + ppc_subf (code, ins->dreg, ins->sreg2, ppc_r11); break; case OP_SBB_IMM: ppc_load (code, ppc_r11, ins->inst_imm); @@ -2590,12 +2593,20 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) ppc_bl (code, 0); break; } + case OP_START_HANDLER: + ppc_mflr (code, ppc_r0); + ppc_stw (code, ppc_r0, ins->inst_left->inst_offset, ins->inst_left->inst_basereg); + break; case OP_ENDFILTER: if (ins->sreg1 != ppc_r3) ppc_mr (code, ppc_r3, ins->sreg1); + ppc_lwz (code, ppc_r0, ins->inst_left->inst_offset, ins->inst_left->inst_basereg); + ppc_mtlr (code, ppc_r0); ppc_blr (code); break; case CEE_ENDFINALLY: + ppc_lwz (code, ppc_r0, ins->inst_left->inst_offset, ins->inst_left->inst_basereg); + ppc_mtlr (code, ppc_r0); ppc_blr (code); break; case OP_CALL_HANDLER: @@ -2645,7 +2656,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_CGT: case OP_CGT_UN: ppc_li (code, ins->dreg, 1); - ppc_bc (code, PPC_BR_TRUE, PPC_BR_LT, 2); + ppc_bc (code, PPC_BR_TRUE, PPC_BR_GT, 2); ppc_li (code, ins->dreg, 0); break; case OP_COND_EXC_EQ: diff --git a/runtime/Makefile.am b/runtime/Makefile.am index a979435d303..3188fcec98e 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -27,7 +27,7 @@ assemblies_DATA = \ Mono.Data.SybaseClient.dll \ Mono.Data.TdsClient.dll \ Mono.Data.Tds.dll \ - Mono.Directory.LDAP.dll \ + Novell.Directory.Ldap.dll \ Mono.GetOptions.dll \ Mono.Http.dll \ Mono.PEToolkit.dll \ diff --git a/web/plans b/web/plans index db6f6dde7c8..f53aecd4e95 100755 --- a/web/plans +++ b/web/plans @@ -8,7 +8,7 @@ Currently you can read our plans for <a href="ado-net.html">ADO.NET</a>, <a - href="asp-net">ASP.NET</a>, <a href="java.html">Java</a> and + href="asp-net.html">ASP.NET</a>, <a href="java.html">Java</a> and <a href="winforms.html">WinForms</a>. diff --git a/web/sqlite b/web/sqlite index 35ee3056896..6730477202c 100755 --- a/web/sqlite +++ b/web/sqlite @@ -82,7 +82,7 @@ { string connectionString = "URI=file:SqliteTest.db"; IDbConnection dbcon; - dbcon = new MySQLConnection(connectionString); + dbcon = new SqliteConnection(connectionString); dbcon.Open(); IDbCommand dbcmd = dbcon.CreateCommand(); // requires a table to be created named employee |