diff -urN ruby-mysql-0.2.6/mysql.rb ruby-mysql-work/mysql.rb
--- ruby-mysql-0.2.6/mysql.rb	2006-08-01 12:35:02.000000000 -0700
+++ ruby-mysql-work/mysql.rb	2006-08-04 17:01:55.000000000 -0700
@@ -9,6 +9,7 @@
   VERSION = "4.0-ruby-0.2.5"
 
   require "socket"
+  require "digest/sha1"
 
   MAX_PACKET_LENGTH = 256*256*256-1
   MAX_ALLOWED_PACKET = 1024*1024*1024
@@ -17,6 +18,11 @@
   MYSQL_PORT = 3306
   PROTOCOL_VERSION = 10
 
+  SCRAMBLE_LENGTH = 20
+  SCRAMBLE_LENGTH_323 = 8
+
+  SQLSTATE_LENGTH = 5
+
   # Command
   COM_SLEEP		= 0
   COM_QUIT		= 1
@@ -40,6 +46,14 @@
   COM_TABLE_DUMP	= 19
   COM_CONNECT_OUT	= 20
   COM_REGISTER_SLAVE	= 21
+  COM_STMT_PREPARE      = 22
+  COM_STMT_EXECUTE      = 23
+  COM_STMT_SEND_LONG_DATA= 24
+  COM_STMT_CLOSE        = 25
+  COM_STMT_RESET        = 26
+  COM_SET_OPTION        = 27
+  COM_STMT_FETCH        = 28
+  COM_DAEMON            = 29
 
   # Client flag
   CLIENT_LONG_PASSWORD	= 1
@@ -51,10 +65,15 @@
   CLIENT_ODBC		= 1 << 6
   CLIENT_LOCAL_FILES	= 1 << 7
   CLIENT_IGNORE_SPACE	= 1 << 8
+  CLIENT_PROTOCOL_41 	= 1 << 9
   CLIENT_INTERACTIVE	= 1 << 10
   CLIENT_SSL		= 1 << 11
   CLIENT_IGNORE_SIGPIPE	= 1 << 12
   CLIENT_TRANSACTIONS	= 1 << 13
+  CLIENT_RESERVED    	= 1 << 14
+  CLIENT_SECURE_CONNECTION = 1 << 15
+  CLIENT_MULTI_STATEMENTS = 1 << 16
+  CLIENT_MULTI_RESULTS  = 1 << 17
   CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
 
   # Connection Option
@@ -67,10 +86,31 @@
   SET_CHARSET_DIR	= 6
   SET_CHARSET_NAME	= 7
   OPT_LOCAL_INFILE	= 8
+  OPT_PROTOCOL          = 9
+  SHARED_MEMORY_BASE_NAME= 10
+  OPT_READ_TIMEOUT      = 11
+  OPT_WRITE_TIMEOUT     = 12
+  OPT_USE_RESULT        = 13
+  OPT_USE_REMOTE_CONNECTION= 14
+  OPT_USE_EMBEDDED_CONNECTION= 15
+  OPT_GUESS_CONNECTION  = 16
+  OPT_SET_CLIENT_IP     = 17
+  SECURE_AUTH           = 18
+  REPORT_DATA_TRUNCATION= 19
+  OPT_RECONNECT         = 20
+  OPT_SSL_VERIFY_SERVER_CERT= 21
 
   # Server Status
-  SERVER_STATUS_IN_TRANS	= 1
-  SERVER_STATUS_AUTOCOMMIT	= 2
+  SERVER_STATUS_IN_TRANS	= 1   # Transaction has started
+  SERVER_STATUS_AUTOCOMMIT	= 2   # Server in auto_commit mode
+  SERVER_STATUS_MORE_RESULTS	= 4   # More results on server
+  SERVER_MORE_RESULTS_EXISTS	= 8   # Multi query - next query exists
+  SERVER_QUERY_NO_GOOD_INDEX_USED= 16
+  SERVER_QUERY_NO_INDEX_USED	= 32
+  SERVER_STATUS_CURSOR_EXISTS	= 64  # Cursor was opened for query
+  SERVER_STATUS_LAST_ROW_SENT	= 128 # Read-only cursor was exhausted
+  SERVER_STATUS_DB_DROPPED	= 256 # A database was dropped
+  SERVER_STATUS_NO_BACKSLASH_ESCAPES = 512
 
   # Refresh parameter
   REFRESH_GRANT		= 1
@@ -81,6 +121,18 @@
   REFRESH_THREADS	= 32
   REFRESH_SLAVE		= 64
   REFRESH_MASTER	= 128
+  REFRESH_QUERY_CACHE	= 65536
+  REFRESH_QUERY_CACHE_FREE = 0x20000
+  REFRESH_DES_KEY_FILE	= 0x40000
+  REFRESH_USER_RESOURCES= 0x80000
+
+  # Shutdown parameter
+  SHUTDOWN_DEFAULT	= 0
+  SHUTDOWN_WAIT_CONNECTIONS = 1
+  SHUTDOWN_WAIT_TRANSACTIONS = 2
+  SHUTDOWN_WAIT_UPDATES = 8
+  SHUTDOWN_WAIT_ALL_BUFFERS = 16
+  SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17
 
   def initialize(*args)
     @client_flag = 0
@@ -92,6 +144,10 @@
     end
   end
 
+  def protocol_41?()
+    @server_capabilities & CLIENT_PROTOCOL_41 != 0
+  end
+
   def real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=nil)
     @server_status = SERVER_STATUS_AUTOCOMMIT
     if (host == nil or host == "localhost") and defined? UNIXSocket then
@@ -115,25 +171,68 @@
       @server_capabilities, = a.slice!(0,2).unpack("v")
     end
     if a.size >= 16 then
-      @server_language, @server_status = a.unpack("cv")
+      @server_language, @server_status = a.slice!(0,3).unpack("cv")
+      # bytes reserved for future use
+      a.slice!(0,13)
+    end
+
+    if a.size >= SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 then
+      @scramble_buff << a.slice!(0, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323)
+    else
+      @server_capabilities = @server_capabilities & ~CLIENT_SECURE_CONNECTION
     end
 
     flag = 0 if flag == nil
     flag |= @client_flag | CLIENT_CAPABILITIES
-    flag |= CLIENT_CONNECT_WITH_DB if db
-    data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+(user||"")+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)
+
+    if @server_capabilities & CLIENT_SECURE_CONNECTION != 0 then
+      flag |= CLIENT_SECURE_CONNECTION
+    end
+
+    if @server_capabilities & CLIENT_PROTOCOL_41 != 0 then
+      flag |= CLIENT_PROTOCOL_41
+      data = Net::int4str(flag) + Net::int4str(@max_allowed_packet)
+      # we just specify latin1 as the charset for now
+      data << 8
+      # bytes reserved for future use
+      data << "\0" * 23
+    else
+      data = Net::int2str(flag) + Net::int3str(@max_allowed_packet)
+    end
+
+    data << (user || "") + "\0"
+    if flag & CLIENT_SECURE_CONNECTION != 0 then
+      data << scramble(passwd, @scramble_buff)
+    else
+      data << scramble_323(passwd, @scramble_buff, @protocol_version==9)
+    end
+
     if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
+      flag |= CLIENT_CONNECT_WITH_DB
       data << "\0"+db
       @db = db.dup
     end
-    print "data: ", data.unpack("H*"), "\n"
+
     write data
-    read
+    pkt = read
+
+    handle_auth_fallback(pkt, passwd)
+
     ObjectSpace.define_finalizer(self, Mysql.finalizer(@net))
     self
   end
   alias :connect :real_connect
 
+  def handle_auth_fallback(pkt, passwd)
+    # A packet like this means that we need to send an old-format password
+    if pkt.size == 1 and pkt[0] == 254 and
+       @server_capabilities & CLIENT_SECURE_CONNECTION != 0 then
+      data = scramble_323(passwd, @scramble_buff, @protocol_version == 9)
+      write data + "\0"
+      read
+    end
+  end
+
   def escape_string(str)
     Mysql::escape_string str
   end
@@ -184,8 +283,17 @@
   end
 
   def change_user(user="", passwd="", db="")
-    data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
-    command COM_CHANGE_USER, data
+    data = user + "\0"
+    if protocol_41? then
+      data << scramble(passwd, @scramble_buff)
+    else
+      data << scramble_323(passwd, @scramble_buff, @protocol_version==9) + "\0"
+    end
+    data << db
+
+    pkt = command COM_CHANGE_USER, data
+    handle_auth_fallback(pkt, passwd)
+
     @user = user
     @passwd = passwd
     @db = db
@@ -246,7 +354,7 @@
   def list_fields(table, field=nil)
     command COM_FIELD_LIST, "#{table}\0#{field}", true
     f = read_rows 6
-    fields = unpack_fields(f, @server_capabilities & CLIENT_LONG_FLAG != 0)
+    fields = unpack_fields(f)
     res = Result::new self, fields, f.length
     res.eof = true
     res
@@ -256,7 +364,7 @@
     data = command COM_PROCESS_INFO
     @field_count = get_length data
     fields = read_rows 5
-    @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
+    @fields = unpack_fields(fields)
     @status = :STATUS_GET_RESULT
     store_result
   end
@@ -299,8 +407,8 @@
     self
   end
 
-  def shutdown()
-    command COM_SHUTDOWN
+  def shutdown(level = SHUTDOWN_DEFAULT)
+    command COM_SHUTDOWN, level
     self
   end
 
@@ -313,7 +421,15 @@
 
   def read_one_row(field_count)
     data = read
-    return if data[0] == 254 and data.length == 1
+    if data[0] == 254 and data.length <= 8 then
+      if protocol_41? then
+	a = data.slice!(1,2)
+	@warning_count = a[0]+a[1]*256
+	a = data.slice!(1,2)
+	@server_status = a[0]+a[1]*256
+      end
+    end
+    return if data[0] == 254 and data.length <= 8
     rec = []
     field_count.times do
       len = get_length data
@@ -356,39 +472,64 @@
     if @field_count == 0 then
       @affected_rows = get_length(data, true)
       @insert_id = get_length(data, true)
-      if @server_capabilities & CLIENT_TRANSACTIONS != 0 then
+      if protocol_41? then
+	a = data.slice!(0,2)
+	@server_status = a[0]+a[1]*256
+	a = data.slice!(0,2)
+	@warning_count = a[0]+a[1]*256
+      elsif @server_capabilities & CLIENT_TRANSACTIONS != 0 then
 	a = data.slice!(0,2)
 	@server_status = a[0]+a[1]*256
+        @warning_count = 0
       end
       if data.size > 0 and get_length(data) then
 	@info = data
       end
     else
       @extra_info = get_length(data, true)
-      fields = read_rows 5
-      @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
+      columns = protocol_41? ? 7 : 5
+      fields = read_rows columns
+      @fields = unpack_fields(fields)
       @status = :STATUS_GET_RESULT
     end
     self
   end
 
-  def unpack_fields(data, long_flag_protocol)
+  def unpack_fields(data)
     ret = []
     data.each do |f|
-      table = org_table = f[0]
-      name = f[1]
-      length = f[2][0]+f[2][1]*256+f[2][2]*256*256
-      type = f[3][0]
-      if long_flag_protocol then
-	flags = f[4][0]+f[4][1]*256
-	decimals = f[4][2]
+      if protocol_41? then
+        catalog = f[0]
+        db = f[1]
+        table = f[2]
+        org_table = f[3]
+        name = f[4]
+        org_name = f[5]
+        charset = f[6][0] + f[6][1] * 256
+        length = f[6][2]+f[6][3]*256+f[6][4]*256*256+f[6][5]*256*256*256
+        type = f[6][6]
+        flags = f[6][7]+f[6][8]*256
+        decimals = f[6][9]
+        def_value = f[7]
+        max_length = 0
+        ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
       else
-	flags = f[4][0]
-	decimals = f[4][1]
+        catalog = db = ""
+        table = org_table = f[0]
+        name = f[1]
+        length = f[2][0]+f[2][1]*256+f[2][2]*256*256
+        type = f[3][0]
+        if @server_capabilities & CLIENT_LONG_FLAG != 0 then
+          flags = f[4][0]+f[4][1]*256
+          decimals = f[4][2]
+        else
+          flags = f[4][0]
+          decimals = f[4][1]
+        end
+        def_value = f[5]
+        max_length = 0
+        ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
       end
-      def_value = f[5]
-      max_length = 0
-      ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
     end
     ret
   end
@@ -447,9 +588,14 @@
       if a.length > 3 then
 	@errno = a[1]+a[2]*256
 	@error = a[3 .. -1]
+        if protocol_41? and @error[0] == "#" then
+          @sql_state = @error.slice(1, SQLSTATE_LENGTH)
+          @error.slice!(0, SQLSTATE_LENGTH + 1)
+        end
       else
 	@errno = Error::CR_UNKNOWN_ERROR
 	@error = Error::err @errno
+        @sql_state = "HY000"
       end
       raise Error::new(@errno, @error)
     end
@@ -463,6 +609,17 @@
     @net.write arg
   end
 
+  def scramble(password, message)
+    return "\0" if password == nil or password == ""
+    stage1 = Digest::SHA1.digest(password)
+    stage2 = Digest::SHA1.digest(stage1)
+    result = Digest::SHA1.digest(message + stage2)
+    0.upto(SCRAMBLE_LENGTH - 1) { |i|
+      result[i] = result[i] ^ stage1[i]
+    }
+    return result.length.chr + result
+  end
+
   def hash_password(password)
     nr = 1345345333
     add = 7
@@ -476,14 +633,14 @@
     [nr & ((1 << 31) - 1), nr2 & ((1 << 31) - 1)]
   end
 
-  def scramble(password, message, old_ver)
+  def scramble_323(password, message, old_ver)
     return "" if password == nil or password == ""
     raise "old version password is not implemented" if old_ver
     hash_pass = hash_password password
-    hash_message = hash_password message
+    hash_message = hash_password message.slice(0,SCRAMBLE_LENGTH_323)
     rnd = Random::new hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]
     to = []
-    1.upto(message.length) do
+    1.upto(SCRAMBLE_LENGTH_323) do
       to << ((rnd.rnd*31)+64).floor
     end
     extra = (rnd.rnd*31).floor
@@ -656,9 +813,6 @@
     TIMESTAMP_FLAG  = 1024
     SET_FLAG = 2048
     NUM_FLAG = 32768
-    PART_KEY_FLAG = 16384
-    GROUP_FLAG = 32768
-    UNIQUE_FLAG = 65536
 
     def initialize(table, org_table, name, length, type, flags, decimals, def_value, max_length)
       @table = table
diff -urN ruby-mysql-0.2.6/t/05change_user.rb ruby-mysql-work/t/05change_user.rb
--- ruby-mysql-0.2.6/t/05change_user.rb	1969-12-31 16:00:00.000000000 -0800
+++ ruby-mysql-work/t/05change_user.rb	2006-08-04 16:05:05.000000000 -0700
@@ -0,0 +1,5 @@
+# use a user-variable to make sure we've actually changed users
+$my.query("set @a = 5")
+$my.change_user($user, $passwd)
+res = $my.query("select @a")
+if res.fetch_row != [nil] then raise "change_user must have failed" end

