--- mysql_org.rb	2005-04-03 20:59:15.830032000 +0900
+++ mysql4.rb	2005-04-03 21:20:39.696140800 +0900
@@ -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
@@ -51,11 +52,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_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
+  PROTO_AUTH41 = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION
 
   # Connection Option
   OPT_CONNECT_TIMEOUT	= 0
@@ -115,13 +120,26 @@
       @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")
     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 & PROTO_AUTH41
+      data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+
+             (user||"")+"\0"+
+                   scramble(passwd, @scramble_buff, @protocol_version==9)
+    else
+      dummy, @salt2 = a.unpack("a13a12")
+      @scramble_buff += @salt2
+      flag |= PROTO_AUTH41
+      data = Net::int4str(flag) + Net::int4str(@max_allowed_packet) +
+             ([8] + Array.new(23, 0)).pack("c24") + (user||"")+"\0"+
+             scramble41(passwd, @scramble_buff)
+    end
+    
     if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
       data << "\0"+db
       @db = db.dup
@@ -183,7 +201,11 @@
   end
 
   def change_user(user="", passwd="", db="")
+    if !@server_capabilities & PROTO_AUTH41
     data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
+    else
+      data = user+"\0"+ scramble41(passwd, @scramble_buff)
+    end
     command COM_CHANGE_USER, data
     @user = user
     @passwd = passwd
@@ -244,7 +266,11 @@
 
   def list_fields(table, field=nil)
     command COM_FIELD_LIST, "#{table}\0#{field}", true
+    if !@server_capabilities & PROTO_AUTH41
     f = read_rows 6
+    else
+      f = read_rows 7
+    end
     fields = unpack_fields(f, @server_capabilities & CLIENT_LONG_FLAG != 0)
     res = Result::new self, fields, f.length
     res.eof = true
@@ -254,7 +280,11 @@
   def list_processes()
     data = command COM_PROCESS_INFO
     @field_count = get_length data
+    if !@server_capabilities & PROTO_AUTH41
     fields = read_rows 5
+    else
+      fields = read_rows 7
+    end
     @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
     @status = :STATUS_GET_RESULT
     store_result
@@ -312,7 +342,11 @@
 
   def read_one_row(field_count)
     data = read
-    return if data[0] == 254 and data.length == 1
+    if data[0] == 254 and data.length == 1 ## EOF
+      return
+    elsif data[0] == 254 and data.length == 5
+      return
+    end
     rec = []
     field_count.times do
       len = get_length data
@@ -364,7 +398,11 @@
       end
     else
       @extra_info = get_length(data, true)
+      if !@server_capabilities & PROTO_AUTH41
       fields = read_rows 5
+      else
+        fields = read_rows(7)
+      end
       @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
       @status = :STATUS_GET_RESULT
     end
@@ -374,6 +412,7 @@
   def unpack_fields(data, long_flag_protocol)
     ret = []
     data.each do |f|
+      if !@server_capabilities & PROTO_AUTH41
       table = org_table = f[0]
       name = f[1]
       length = f[2][0]+f[2][1]*256+f[2][2]*256*256
@@ -387,8 +426,22 @@
       end
       def_value = f[5]
       max_length = 0
+      else
+        catalog = f[0]
+        db = f[1]
+        table = f[2]
+        org_table = f[3]
+        name = f[4]
+        org_name = f[5]
+        length = f[6][2]+f[6][3]*256+f[6][4]*256*256
+        type = f[6][6]
+        flags = f[6][7]+f[6][8]*256
+        decimals = f[6][9]
+        def_value = ""
+        max_length = 0
       ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
     end
+    end
     ret
   end
 
@@ -490,6 +543,19 @@
     to.join
   end
 
+  def scramble41(password, message)
+    if password.length != 0
+      buf = [0x14]
+      s1 = Digest::SHA1.new(password).digest
+      s2 = Digest::SHA1.new(s1).digest
+      x = Digest::SHA1.new(message + s2).digest
+      (0..s1.length - 1).each {|i| buf.push(s1[i] ^ x[i])}
+      buf.pack("C*")
+    else
+      0x00.chr
+    end
+  end
+
   def error(errno)
     @errno = errno
     @error = Error::err errno
