[Zope-Checkins] CVS: Zope2 - async_mysql.py:1.3 recorder.py:1.3 syncsock.py:1.3

andreas@serenade.digicool.com andreas@serenade.digicool.com
Tue, 1 May 2001 07:45:57 -0400


Update of /cvs-repository/Zope2/ZServer/medusa/misc
In directory serenade.digicool.com:/tmp/cvs-serv12359/misc

Modified Files:
	async_mysql.py recorder.py syncsock.py 
Log Message:
we *hate* tabs - lets get rid of them



--- Updated File async_mysql.py in package Zope2 --
--- async_mysql.py	2001/04/25 19:09:49	1.2
+++ async_mysql.py	2001/05/01 11:45:26	1.3
@@ -15,484 +15,484 @@
 from fifo import fifo
 
 class mysql_error (exceptions.Exception):
-	pass
-
-# ===========================================================================
-#						   Authentication
-# ===========================================================================
-
-# Note: I've ignored the stuff to support an older version of the protocol.
-#
-# The code is based on the file mysql-3.21.33/client/password.c
-#
-# The auth scheme is challenge/response.  Upon connection the server
-# sends an 8-byte challenge message.  This is hashed with the password
-# to produce an 8-byte response.  The server side performs an identical
-# hash to verify the password is correct.
-
+    pass
+    
+    # ===========================================================================
+    #						   Authentication
+    # ===========================================================================
+    
+    # Note: I've ignored the stuff to support an older version of the protocol.
+    #
+    # The code is based on the file mysql-3.21.33/client/password.c
+    #
+    # The auth scheme is challenge/response.  Upon connection the server
+    # sends an 8-byte challenge message.  This is hashed with the password
+    # to produce an 8-byte response.  The server side performs an identical
+    # hash to verify the password is correct.
+    
 class random_state:
-
-	def __init__ (self, seed, seed2):
-		self.max_value = 0x3FFFFFFF
-		self.seed = seed % self.max_value
-		self.seed2 = seed2 % self.max_value
-		
-	def rnd (self):
-		self.seed = (self.seed * 3 + self.seed2) % self.max_value
-		self.seed2 = (self.seed + self.seed2 + 33) % self.max_value
-		return float(self.seed)/ float(self.max_value)
 
+    def __init__ (self, seed, seed2):
+        self.max_value = 0x3FFFFFFF
+        self.seed = seed % self.max_value
+        self.seed2 = seed2 % self.max_value
+        
+    def rnd (self):
+        self.seed = (self.seed * 3 + self.seed2) % self.max_value
+        self.seed2 = (self.seed + self.seed2 + 33) % self.max_value
+        return float(self.seed)/ float(self.max_value)
+        
 def hash_password (password):
-	nr=1345345333L
-	add=7
-	nr2=0x12345671L
-	for ch in password:
-		if (ch == ' ') or (ch == '\t'):
-			continue
-		tmp = ord(ch)
-		nr = nr ^ (((nr & 63) + add) * tmp) + (nr << 8)
-		nr2 = nr2 + ((nr2 << 8) ^ nr)
-		add = add + tmp
-	return (
-		nr & ((1L<<31)-1L),
-		nr2 & ((1L<<31)-1L)
-		)
-
+    nr=1345345333L
+    add=7
+    nr2=0x12345671L
+    for ch in password:
+        if (ch == ' ') or (ch == '\t'):
+            continue
+        tmp = ord(ch)
+        nr = nr ^ (((nr & 63) + add) * tmp) + (nr << 8)
+        nr2 = nr2 + ((nr2 << 8) ^ nr)
+        add = add + tmp
+    return (
+            nr & ((1L<<31)-1L),
+            nr2 & ((1L<<31)-1L)
+            )
+    
 def scramble (message, password):
-	hash_pass = hash_password (password)
-	hash_mess = hash_password (message)
-	r = random_state (
-		hash_pass[0] ^ hash_mess[0],
-		hash_pass[1] ^ hash_mess[1]
-		)
-	to = []
-	for ch in message:
-		to.append (int (math.floor ((r.rnd() * 31) + 64)))
-	extra = int (math.floor (r.rnd()*31))
-	for i in range(len(to)):
-		to[i] = to[i] ^ extra
-	return to
-
-# ===========================================================================
-#						   Packet Protocol
-# ===========================================================================
-
+    hash_pass = hash_password (password)
+    hash_mess = hash_password (message)
+    r = random_state (
+            hash_pass[0] ^ hash_mess[0],
+            hash_pass[1] ^ hash_mess[1]
+            )
+    to = []
+    for ch in message:
+        to.append (int (math.floor ((r.rnd() * 31) + 64)))
+    extra = int (math.floor (r.rnd()*31))
+    for i in range(len(to)):
+        to[i] = to[i] ^ extra
+    return to
+    
+    # ===========================================================================
+    #						   Packet Protocol
+    # ===========================================================================
+    
 def unpacket (p):
-	# 3-byte length, one-byte packet number, followed by packet data
-	a,b,c,s = map (ord, p[:4])
-	l = a | (b << 8) | (c << 16)
-	# s is a sequence number
-	return l, s
-
+        # 3-byte length, one-byte packet number, followed by packet data
+    a,b,c,s = map (ord, p[:4])
+    l = a | (b << 8) | (c << 16)
+    # s is a sequence number
+    return l, s
+    
 def packet (data, s=0):
-	l = len(data)
-	a, b, c = l & 0xff, (l>>8) & 0xff, (l>>16) & 0xff
-	h = map (chr, [a,b,c,s])
-	return string.join (h,'') + data
-
+    l = len(data)
+    a, b, c = l & 0xff, (l>>8) & 0xff, (l>>16) & 0xff
+    h = map (chr, [a,b,c,s])
+    return string.join (h,'') + data
+    
 def n_byte_num (data, n):
-	result = 0
-	for i in range(n):
-		result = result | (ord(data[i])<<(8*i))
-	return result
-
+    result = 0
+    for i in range(n):
+        result = result | (ord(data[i])<<(8*i))
+    return result
+    
 def net_field_length (data, pos=0):
-	n = ord(data[pos])
-	if n < 251:
-		return n, 1
-	elif n == 251:
-		return None, 1
-	elif n == 252:
-		return n_byte_num (data, 2), 3
-	elif n == 253:
-		return n_byte_num (data, 3), 4
-	else:
-		# libmysql adds 6, why?
-		return n_byte_num (data, 4), 5
-
-# used to generate the dumps below
+    n = ord(data[pos])
+    if n < 251:
+        return n, 1
+    elif n == 251:
+        return None, 1
+    elif n == 252:
+        return n_byte_num (data, 2), 3
+    elif n == 253:
+        return n_byte_num (data, 3), 4
+    else:
+            # libmysql adds 6, why?
+        return n_byte_num (data, 4), 5
+        
+        # used to generate the dumps below
 def dump_hex (s):
-	r1 = []
-	r2 = []
-	for ch in s:
-		r1.append (' %02x' % ord(ch))
-		if (ch in string.letters) or (ch in string.digits):
-			r2.append ('  %c' % ch)
-		else:
-			r2.append ('   ')
-	return string.join (r1, ''), string.join (r2, '')
-
-# ===========================================================================
-#							 MySQL Client
-# ===========================================================================
-
+    r1 = []
+    r2 = []
+    for ch in s:
+        r1.append (' %02x' % ord(ch))
+        if (ch in string.letters) or (ch in string.digits):
+            r2.append ('  %c' % ch)
+        else:
+            r2.append ('   ')
+    return string.join (r1, ''), string.join (r2, '')
+    
+    # ===========================================================================
+    #							 MySQL Client
+    # ===========================================================================
+    
 class mysql_client (asynchat.async_chat):
-	
-	# protocol state
-	PS_HEADER		= 1
-	PS_DATA			= 2
-
-	# auth state
-	AS_LOGIN		= 1
-	AS_CHALLENGE	= 2
-	AS_RESPONSE		= 3
-
-	def __init__ (self, username, password, login_callback, address=('127.0.0.1', 3306)):
-		asynchat.async_chat.__init__ (self)
-		self.username = username
-		self.password = password
-		self.server_address = address
-		self.login (login_callback)
-		self.current_database = None
-
-	def login (self, callback):
-		self.login_callback = callback
-		self.buffer = ''
-		self.set_terminator (None)
-		address = self.server_address
-		if type(address) == type(''):
-			self.create_socket (socket.AF_UNIX, socket.SOCK_STREAM)
-		else:
-			self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
-		self._connected = 0
-		self.connect (address)
-		self.auth = self.AS_LOGIN
-		self.state = self.PS_HEADER
-		self.query_fifo = fifo()
-
-	def handle_connect (self):
-		self._connected = 1
-
-	def close (self):
-		asynchat.async_chat.close (self)
-		self._connected = 0
-		self.discard_buffers()
-		# XXX: check query fifo for pending queries
-
-	def collect_incoming_data (self, data):
-		# packets come in with a four-byte head on them.
-		# we need to stay sync'd with that.	 we use a
-		# two-state machine.
-		self.buffer = self.buffer + data
-		while self.buffer:
-			if self.state is self.PS_HEADER:
-				# do we have a complete header?
-				if len(self.buffer) >= 4:
-					l,s = unpacket (self.buffer)
-					self.pinfo = l, s
-					self.state = self.PS_DATA
-					self.buffer = self.buffer[4:]
-				else:
-					break
-			elif self.state is self.PS_DATA:
-				l, s = self.pinfo
-				if len(self.buffer) >= l:
-					data, self.buffer = self.buffer[:l], self.buffer[l:]
-					self.handle_packet (s, data)
-					self.state = self.PS_HEADER
-				else:
-					break
-
-	def handle_packet (self, seq, data):
-		if self.auth is self.AS_LOGIN:
-			# unpack the greeting
-			protocol_version = ord(data[0])
-			eos = string.find (data, '\000')
-			mysql_version = data[1:eos]
-			thread_id = n_byte_num (data[eos+1:eos+5], 4)
-			challenge = data[eos+5:eos+13]
-			self.auth = (
-				protocol_version,
-				mysql_version,
-				thread_id,
-				challenge
-				)
-			# print auth
-			lp = self.build_login_packet (challenge)
-			# print 'login packet:',repr(lp)
-			self.auth = self.AS_CHALLENGE
-			# seems to require a sequence number of one
-			self.push (packet (lp, 1))
-		elif self.auth is self.AS_CHALLENGE:
-			if seq != 2:
-				self.login_callback (self, 0)
-			else:
-				self.auth = self.AS_RESPONSE
-				self.login_callback (self, 1)
-		else:
-			if seq == 1:
-				callback = self.query_fifo.pop()
-				if callback:
-					self.current_callback = callback
-					callback (seq, data)
-			else:
-				if self.current_callback:
-					self.current_callback (seq, data)
-
-	def build_login_packet (self, challenge):
-		auth = string.join (map (chr, scramble (challenge, self.password)), '')
-		# 2 bytes of client_capability
-		# 3 bytes of max_allowed_packet
-		# no idea what they are
-		return '\005\000\000\000\020' + self.username + '\000' + auth
-
-	def push_query (self, query, callback=None, sequence=0):
-		if self._connected:
-			self.push (packet (query, sequence))
-			self.query_fifo.push (callback)
-		else:
-			self.login (
-				continuation (self._relogin, (query, callback, sequence))
-				)
-
-	# ======================================================================
-	#					auto-reconnect support
-	# ======================================================================
-
-	def _relogin (self, (query, callback, sequence), ignore, result):
-		if result:
-			if self.current_database:
-				self.cmd_use (
-					self.current_database,
-					continuation (self._relogin_use, (query, callback, sequence))
-					)
-			else:
-				self.push_query (query, callback, sequence)
-		else:
-			# XXX all callbacks need an 'error' parameter.
-			raise SystemError, "Couldn't reconnect to mysql server"
-
-	def _relogin_use (self, (query, callback, sequence), nfields, data):
-		# this should really be done by cmd_use... (i.e., use a continuation object)
-		if (nfields > 0) and (data == '\000\000\000'):
-			self.push_query (query, callback, sequence)
-		else:
-			# XXX all callbacks need an 'error' parameter.
-			raise SystemError, "Couldn't reconnect to current database"
-
-	# ======================================================================
-	#						   Commands
-	# ======================================================================
-	
-	# from mysql-3.21.33/include/mysql_com.h.in
-	#
-
-	cmds = [
-		'sleep', 'quit', 'init_db', 'query', 'field_list', 'create_db',
-		'drop_db', 'refresh', 'shutdown', 'statistics', 'process_info',
-		'connect', 'process_kill', 'debug'
-		]
-
-	d = {}
-	for i in range (len (cmds)):
-		d[cmds[i]] = i
-	cmds = d
-	del d
 
-	def command (self, command_type, command, callback):
-		self.push_query (
-			chr(self.cmds[command_type]) + command,
-			callback
-			)
-
-	def cmd_use (self, database, callback=None):
-		self.current_database = database
-		self.command ('init_db', database, callback)
-
-	def cmd_quit (self, callback=None):
-		self.command ('quit', '', callback)
-
-	def cmd_query (self, query, callback=None):
-		self.command ('query', query, result_set (callback))
-
-# ===========================================================================
-#							  Result Set
-# ===========================================================================
-
+        # protocol state
+    PS_HEADER		= 1
+    PS_DATA			= 2
+    
+    # auth state
+    AS_LOGIN		= 1
+    AS_CHALLENGE	= 2
+    AS_RESPONSE		= 3
+    
+    def __init__ (self, username, password, login_callback, address=('127.0.0.1', 3306)):
+        asynchat.async_chat.__init__ (self)
+        self.username = username
+        self.password = password
+        self.server_address = address
+        self.login (login_callback)
+        self.current_database = None
+        
+    def login (self, callback):
+        self.login_callback = callback
+        self.buffer = ''
+        self.set_terminator (None)
+        address = self.server_address
+        if type(address) == type(''):
+            self.create_socket (socket.AF_UNIX, socket.SOCK_STREAM)
+        else:
+            self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
+        self._connected = 0
+        self.connect (address)
+        self.auth = self.AS_LOGIN
+        self.state = self.PS_HEADER
+        self.query_fifo = fifo()
+        
+    def handle_connect (self):
+        self._connected = 1
+        
+    def close (self):
+        asynchat.async_chat.close (self)
+        self._connected = 0
+        self.discard_buffers()
+        # XXX: check query fifo for pending queries
+        
+    def collect_incoming_data (self, data):
+            # packets come in with a four-byte head on them.
+            # we need to stay sync'd with that.	 we use a
+            # two-state machine.
+        self.buffer = self.buffer + data
+        while self.buffer:
+            if self.state is self.PS_HEADER:
+                    # do we have a complete header?
+                if len(self.buffer) >= 4:
+                    l,s = unpacket (self.buffer)
+                    self.pinfo = l, s
+                    self.state = self.PS_DATA
+                    self.buffer = self.buffer[4:]
+                else:
+                    break
+            elif self.state is self.PS_DATA:
+                l, s = self.pinfo
+                if len(self.buffer) >= l:
+                    data, self.buffer = self.buffer[:l], self.buffer[l:]
+                    self.handle_packet (s, data)
+                    self.state = self.PS_HEADER
+                else:
+                    break
+                    
+    def handle_packet (self, seq, data):
+        if self.auth is self.AS_LOGIN:
+                # unpack the greeting
+            protocol_version = ord(data[0])
+            eos = string.find (data, '\000')
+            mysql_version = data[1:eos]
+            thread_id = n_byte_num (data[eos+1:eos+5], 4)
+            challenge = data[eos+5:eos+13]
+            self.auth = (
+                    protocol_version,
+                    mysql_version,
+                    thread_id,
+                    challenge
+                    )
+            # print auth
+            lp = self.build_login_packet (challenge)
+            # print 'login packet:',repr(lp)
+            self.auth = self.AS_CHALLENGE
+            # seems to require a sequence number of one
+            self.push (packet (lp, 1))
+        elif self.auth is self.AS_CHALLENGE:
+            if seq != 2:
+                self.login_callback (self, 0)
+            else:
+                self.auth = self.AS_RESPONSE
+                self.login_callback (self, 1)
+        else:
+            if seq == 1:
+                callback = self.query_fifo.pop()
+                if callback:
+                    self.current_callback = callback
+                    callback (seq, data)
+            else:
+                if self.current_callback:
+                    self.current_callback (seq, data)
+                    
+    def build_login_packet (self, challenge):
+        auth = string.join (map (chr, scramble (challenge, self.password)), '')
+        # 2 bytes of client_capability
+        # 3 bytes of max_allowed_packet
+        # no idea what they are
+        return '\005\000\000\000\020' + self.username + '\000' + auth
+        
+    def push_query (self, query, callback=None, sequence=0):
+        if self._connected:
+            self.push (packet (query, sequence))
+            self.query_fifo.push (callback)
+        else:
+            self.login (
+                    continuation (self._relogin, (query, callback, sequence))
+                    )
+            
+            # ======================================================================
+            #					auto-reconnect support
+            # ======================================================================
+            
+    def _relogin (self, (query, callback, sequence), ignore, result):
+        if result:
+            if self.current_database:
+                self.cmd_use (
+                        self.current_database,
+                        continuation (self._relogin_use, (query, callback, sequence))
+                        )
+            else:
+                self.push_query (query, callback, sequence)
+        else:
+                # XXX all callbacks need an 'error' parameter.
+            raise SystemError, "Couldn't reconnect to mysql server"
+            
+    def _relogin_use (self, (query, callback, sequence), nfields, data):
+            # this should really be done by cmd_use... (i.e., use a continuation object)
+        if (nfields > 0) and (data == '\000\000\000'):
+            self.push_query (query, callback, sequence)
+        else:
+                # XXX all callbacks need an 'error' parameter.
+            raise SystemError, "Couldn't reconnect to current database"
+            
+            # ======================================================================
+            #						   Commands
+            # ======================================================================
+            
+            # from mysql-3.21.33/include/mysql_com.h.in
+            #
+            
+    cmds = [
+            'sleep', 'quit', 'init_db', 'query', 'field_list', 'create_db',
+            'drop_db', 'refresh', 'shutdown', 'statistics', 'process_info',
+            'connect', 'process_kill', 'debug'
+            ]
+    
+    d = {}
+    for i in range (len (cmds)):
+        d[cmds[i]] = i
+    cmds = d
+    del d
+    
+    def command (self, command_type, command, callback):
+        self.push_query (
+                chr(self.cmds[command_type]) + command,
+                callback
+                )
+        
+    def cmd_use (self, database, callback=None):
+        self.current_database = database
+        self.command ('init_db', database, callback)
+        
+    def cmd_quit (self, callback=None):
+        self.command ('quit', '', callback)
+        
+    def cmd_query (self, query, callback=None):
+        self.command ('query', query, result_set (callback))
+        
+        # ===========================================================================
+        #							  Result Set
+        # ===========================================================================
+        
 class result_set:
-	
-	'unpack a result set'
-	'  If <data_callback> is specified, it will be called'
-	'  for each element of the result set.  Otherwise, the'
-	'  results will be collected into a list made available'
-	'  to <callback>'
-
-	def __init__ (self, callback, data_callback=None):
-		self.callback = callback
-		self.packets = []
-		self.nfields = None
-		self.fields = []
-		self.data = None
-		self.data_callback = None
-
-	def unpack_data (self, d):
-		r = []
-		i = 0
-		while i < len(d):
-			fl = ord(d[i])
-			i = i + 1
-			r.append (d[i:i+fl])
-			i = i + fl
-		return r
 
-	def __call__ (self, seq, data):
-		if self.nfields is None:
-			# first packet is the number of fields (or an error)
-			n = ord(data[0])
-			if n == 0:
-				self.callback ([], [])
-			else:
-				self.nfields = n
-		elif self.data is None:
-			if ord(data[0]) != 0xfe:
-				# collect field info
-				self.fields.append (self.unpack_data (data))
-			else:
-				self.data = []
-		else:
-			# collect data
-			if ord(data[0]) != 0xfe:
-				if self.data_callback:
-					self.data_callback (self.unpack_data (data))
-				else:
-					self.data.append (self.unpack_data (data))
-			else:
-				self.callback (self.fields, self.data)
-
+    'unpack a result set'
+    '  If <data_callback> is specified, it will be called'
+    '  for each element of the result set.  Otherwise, the'
+    '  results will be collected into a list made available'
+    '  to <callback>'
+    
+    def __init__ (self, callback, data_callback=None):
+        self.callback = callback
+        self.packets = []
+        self.nfields = None
+        self.fields = []
+        self.data = None
+        self.data_callback = None
+        
+    def unpack_data (self, d):
+        r = []
+        i = 0
+        while i < len(d):
+            fl = ord(d[i])
+            i = i + 1
+            r.append (d[i:i+fl])
+            i = i + fl
+        return r
+        
+    def __call__ (self, seq, data):
+        if self.nfields is None:
+                # first packet is the number of fields (or an error)
+            n = ord(data[0])
+            if n == 0:
+                self.callback ([], [])
+            else:
+                self.nfields = n
+        elif self.data is None:
+            if ord(data[0]) != 0xfe:
+                    # collect field info
+                self.fields.append (self.unpack_data (data))
+            else:
+                self.data = []
+        else:
+                # collect data
+            if ord(data[0]) != 0xfe:
+                if self.data_callback:
+                    self.data_callback (self.unpack_data (data))
+                else:
+                    self.data.append (self.unpack_data (data))
+            else:
+                self.callback (self.fields, self.data)
+                
 if __name__ == '__main__':
-
-	import random
-
-	class test_mysql_client:
-
-		def __init__ (self, client):
-			self.client = client
-			self.client.cmd_query ('create database test_async', self.callback_create)
-
-		def callback_create (self, *info):
-			print 'create database=>', info
-			self.client.cmd_use ('test_async', self.callback_use)
-
-		def callback_use (self, *info):
-			print 'use=>', info
-			self.client.cmd_query (
-				'create table users (name char(30), cool int)',
-				self.callback_create_table
-				)
-
-		people = ['john', 'paul', 'george', 'ringo']
-
-		def callback_create_table (self, *info):
-			print 'create_table=>', info
-			self.people_index = 0
-			self.callback_insert ()
-
-		def callback_insert (self, *info):
-			print 'insert=>', info
-			if self.people_index == len(self.people):
-				self.client.cmd_query (
-					'select * from users',
-					self.callback_query
-					)
-			else:
-				self.client.cmd_query (
-					'insert into users values ("%s", %s)' % (
-						self.people[self.people_index],
-						random.randint (0,1)
-						),
-					self.callback_insert
-					)
-				self.people_index = self.people_index + 1
-
-		def callback_query (self, fields, data):
-			print 'query=>'
-			print ' fields:'
-			for field in fields:
-				print '\t%s' % repr(field)
-			print ' data:'
-			for d in data:
-				print '\t%s' % repr(d)
-
-			self.client.command (
-				'drop_db',
-				'test_async',
-				self.callback_drop
-				)
-
-		def callback_drop (self, *info):
-			print 'drop=>', info
-			self.client.cmd_quit (self.callback_quit)
-			
-		def callback_quit (self, *info):
-			print 'quit=>', info
-
-	def go (client, yesno):
-		if yesno:
-			test_mysql_client (client)
-		else:
-			print 'Failed to log in'
-
-	import sys
-	if len(sys.argv) < 4:
-		print 'Usage: %s <username> <password> <host>' % sys.argv[0]
-	else:
-		[username, password, host]	= sys.argv[1:4]
-		c = mysql_client (username, password, go, (host, 3306))
-		asyncore.loop()
-
-# greeting:
-# * first byte is the protocol version (currently 10)
-# * null-terminated version string
-# * 4-byte thread id.
-# * 8-byte challenge
-# * 2-byte server capabilities?
-
-# message = [0x00, 0x39, 0x4d, 0x59, 0x59, 0x31, 0x29, 0x79, 0x47]
-# password = [0x66, 0x6e, 0x6f, 0x72, 0x64]
-
-# Handshake:
-#----------------------------------------
-#<== 000  0a 33 2e 32 32 2e 31 30 2d 62 65 74 61 00 1b 00 00 00 39 4d 59 59 31 29 79 47 00 0c 00
-#             3     2  2     1  0     b  e  t  a                 9  M  Y  Y  1     y  G         
-#----------------------------------------
-#==> 1
-#    05 00 00 00 10 72 75 73 68 69 6e 67 00 48 51 42 50 5d 4a 54 57
-#                    r  u  s  h  i  n  g     H  Q  B  P     J  T  W
-#----------------------------------------
-#<== 002  00 00 00
-
-# Insertion/Query (no result set)
-#----------------------------------------
-#==> 0
-#    03 69 6e 73 65 72 74 20 69 6e 74 6f 20 75 73 65 72 73 20 76 61 6c 75 65 73 20 28 22 61 73 64 66 40 61 73 64 66 2e 61 73 64 66 22 2c 20 22 6e 22 29
-#        i  n  s  e  r  t     i  n  t  o     u  s  e  r  s     v  a  l  u  e  s           a  s  d  f     a  s  d  f     a  s  d  f              n      
-#----------------------------------------
-#<== 001  00 01 00
-
-# Query (with result set)
-#----------------------------------------
-#==> 0
-#    03 73 65 6c 65 63 74 20 2a 20 66 72 6f 6d 20 75 73 65 72 73
-#        s  e  l  e  c  t           f  r  o  m     u  s  e  r  s
-#----------------------------------------
-#<== 001  02
-#           
-#<== 002  05 75 73 65 72 73 04 6e 61 6d 65 03 80 00 00 01 fe 03 00 00 00
-#             u  s  e  r  s     n  a  m  e                              
-#<== 003  05 75 73 65 72 73 0a 69 73 62 6f 75 6e 63 69 6e 67 03 01 00 00 01 fe 03 00 00 00
-#             u  s  e  r  s     i  s  b  o  u  n  c  i  n  g                              
-#<== 004  fe
-#           
-#<== 005  15 72 75 73 68 69 6e 67 40 6e 69 67 68 74 6d 61 72 65 2e 63 6f 6d 01 6e
-#             r  u  s  h  i  n  g     n  i  g  h  t  m  a  r  e     c  o  m     n
-#<== 006  0e 61 73 64 66 40 61 73 64 66 2e 61 73 64 66 01 6e
-#             a  s  d  f     a  s  d  f     a  s  d  f     n
-#<== 007  fe
-
 
-# "use bouncer_test"
-#==> 0
-#    02 62 6f 75 6e 63 65 72 5f 74 65 73 74
-#        b  o  u  n  c  e  r     t  e  s  t
-#----------------------------------------
-#<== 001  00 00 00
+    import random
+    
+    class test_mysql_client:
+    
+        def __init__ (self, client):
+            self.client = client
+            self.client.cmd_query ('create database test_async', self.callback_create)
+            
+        def callback_create (self, *info):
+            print 'create database=>', info
+            self.client.cmd_use ('test_async', self.callback_use)
+            
+        def callback_use (self, *info):
+            print 'use=>', info
+            self.client.cmd_query (
+                    'create table users (name char(30), cool int)',
+                    self.callback_create_table
+                    )
+            
+        people = ['john', 'paul', 'george', 'ringo']
+        
+        def callback_create_table (self, *info):
+            print 'create_table=>', info
+            self.people_index = 0
+            self.callback_insert ()
+            
+        def callback_insert (self, *info):
+            print 'insert=>', info
+            if self.people_index == len(self.people):
+                self.client.cmd_query (
+                        'select * from users',
+                        self.callback_query
+                        )
+            else:
+                self.client.cmd_query (
+                        'insert into users values ("%s", %s)' % (
+                                self.people[self.people_index],
+                                random.randint (0,1)
+                                ),
+                        self.callback_insert
+                        )
+                self.people_index = self.people_index + 1
+                
+        def callback_query (self, fields, data):
+            print 'query=>'
+            print ' fields:'
+            for field in fields:
+                print '\t%s' % repr(field)
+            print ' data:'
+            for d in data:
+                print '\t%s' % repr(d)
+                
+            self.client.command (
+                    'drop_db',
+                    'test_async',
+                    self.callback_drop
+                    )
+            
+        def callback_drop (self, *info):
+            print 'drop=>', info
+            self.client.cmd_quit (self.callback_quit)
+            
+        def callback_quit (self, *info):
+            print 'quit=>', info
+            
+    def go (client, yesno):
+        if yesno:
+            test_mysql_client (client)
+        else:
+            print 'Failed to log in'
+            
+    import sys
+    if len(sys.argv) < 4:
+        print 'Usage: %s <username> <password> <host>' % sys.argv[0]
+    else:
+        [username, password, host]	= sys.argv[1:4]
+        c = mysql_client (username, password, go, (host, 3306))
+        asyncore.loop()
+        
+        # greeting:
+        # * first byte is the protocol version (currently 10)
+        # * null-terminated version string
+        # * 4-byte thread id.
+        # * 8-byte challenge
+        # * 2-byte server capabilities?
+        
+        # message = [0x00, 0x39, 0x4d, 0x59, 0x59, 0x31, 0x29, 0x79, 0x47]
+        # password = [0x66, 0x6e, 0x6f, 0x72, 0x64]
+        
+        # Handshake:
+        #----------------------------------------
+        #<== 000  0a 33 2e 32 32 2e 31 30 2d 62 65 74 61 00 1b 00 00 00 39 4d 59 59 31 29 79 47 00 0c 00
+        #             3     2  2     1  0     b  e  t  a                 9  M  Y  Y  1     y  G         
+        #----------------------------------------
+        #==> 1
+        #    05 00 00 00 10 72 75 73 68 69 6e 67 00 48 51 42 50 5d 4a 54 57
+        #                    r  u  s  h  i  n  g     H  Q  B  P     J  T  W
+        #----------------------------------------
+        #<== 002  00 00 00
+        
+        # Insertion/Query (no result set)
+        #----------------------------------------
+        #==> 0
+        #    03 69 6e 73 65 72 74 20 69 6e 74 6f 20 75 73 65 72 73 20 76 61 6c 75 65 73 20 28 22 61 73 64 66 40 61 73 64 66 2e 61 73 64 66 22 2c 20 22 6e 22 29
+        #        i  n  s  e  r  t     i  n  t  o     u  s  e  r  s     v  a  l  u  e  s           a  s  d  f     a  s  d  f     a  s  d  f              n      
+        #----------------------------------------
+        #<== 001  00 01 00
+        
+        # Query (with result set)
+        #----------------------------------------
+        #==> 0
+        #    03 73 65 6c 65 63 74 20 2a 20 66 72 6f 6d 20 75 73 65 72 73
+        #        s  e  l  e  c  t           f  r  o  m     u  s  e  r  s
+        #----------------------------------------
+        #<== 001  02
+        #           
+        #<== 002  05 75 73 65 72 73 04 6e 61 6d 65 03 80 00 00 01 fe 03 00 00 00
+        #             u  s  e  r  s     n  a  m  e                              
+        #<== 003  05 75 73 65 72 73 0a 69 73 62 6f 75 6e 63 69 6e 67 03 01 00 00 01 fe 03 00 00 00
+        #             u  s  e  r  s     i  s  b  o  u  n  c  i  n  g                              
+        #<== 004  fe
+        #           
+        #<== 005  15 72 75 73 68 69 6e 67 40 6e 69 67 68 74 6d 61 72 65 2e 63 6f 6d 01 6e
+        #             r  u  s  h  i  n  g     n  i  g  h  t  m  a  r  e     c  o  m     n
+        #<== 006  0e 61 73 64 66 40 61 73 64 66 2e 61 73 64 66 01 6e
+        #             a  s  d  f     a  s  d  f     a  s  d  f     n
+        #<== 007  fe
+        
+        
+        # "use bouncer_test"
+        #==> 0
+        #    02 62 6f 75 6e 63 65 72 5f 74 65 73 74
+        #        b  o  u  n  c  e  r     t  e  s  t
+        #----------------------------------------
+        #<== 001  00 00 00

--- Updated File recorder.py in package Zope2 --
--- recorder.py	2001/04/25 19:09:49	1.2
+++ recorder.py	2001/05/01 11:45:26	1.3
@@ -7,60 +7,60 @@
 import asynchat
 
 class recorder_channel (asyncore.dispatcher):
-	def __init__ (self, sock, addr):
-		asyncore.dispatcher.__init__ (self, sock)
-		self.fd = open ('%s:%d' % addr, 'wb')
-
-	def handle_read (self):
-		data = self.recv (1024)
-		if not data:
-			self.fd.close()
-			self.close()
-		else:
-			self.fd.write (data)
-			self.fd.flush()
-
+    def __init__ (self, sock, addr):
+        asyncore.dispatcher.__init__ (self, sock)
+        self.fd = open ('%s:%d' % addr, 'wb')
+        
+    def handle_read (self):
+        data = self.recv (1024)
+        if not data:
+            self.fd.close()
+            self.close()
+        else:
+            self.fd.write (data)
+            self.fd.flush()
+            
 class recorder_server (asyncore.dispatcher):
-
-	SERVER_IDENT = 'Recorder'
 
-	def __init__ (self, port=8989):
-		self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
-		self.bind (('', port))
-		print '%s started on port %d' % (self.SERVER_IDENT, port)
-		self.listen (5)
-		
-	def handle_accept (self):
-		conn, addr = self.accept()
-		print 'incoming connection',addr
-		recorder_channel (conn, addr)
-
-# force a clean shutdown
+    SERVER_IDENT = 'Recorder'
+    
+    def __init__ (self, port=8989):
+        self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
+        self.bind (('', port))
+        print '%s started on port %d' % (self.SERVER_IDENT, port)
+        self.listen (5)
+        
+    def handle_accept (self):
+        conn, addr = self.accept()
+        print 'incoming connection',addr
+        recorder_channel (conn, addr)
+        
+        # force a clean shutdown
 def shutdown():
-	sm = asyncore.socket_map
-	asyncore.socket_map = {}
-	for s in sm.values():
-		try:
-			s.close()
-		except:
-			pass
-	print 'Done.'
-
-
+    sm = asyncore.socket_map
+    asyncore.socket_map = {}
+    for s in sm.values():
+        try:
+            s.close()
+        except:
+            pass
+    print 'Done.'
+    
+    
 if __name__ == '__main__':
-	import string
-	import sys
-	if len(sys.argv) > 1:
-		port = string.atoi (sys.argv[1])
-	else:
-		port = 8989
-	s = recorder_server (port)
-	try:
-		asyncore.loop()
-	except KeyboardInterrupt:
-		import sys
-		import tb
-		print sys.exc_type, sys.exc_value
-		tb.printtb (sys.exc_traceback)
-		print 'Shutting down due to unhandled exception...'
-		shutdown()
+    import string
+    import sys
+    if len(sys.argv) > 1:
+        port = string.atoi (sys.argv[1])
+    else:
+        port = 8989
+    s = recorder_server (port)
+    try:
+        asyncore.loop()
+    except KeyboardInterrupt:
+        import sys
+        import tb
+        print sys.exc_type, sys.exc_value
+        tb.printtb (sys.exc_traceback)
+        print 'Shutting down due to unhandled exception...'
+        shutdown()

--- Updated File syncsock.py in package Zope2 --
--- syncsock.py	2001/04/25 19:09:49	1.2
+++ syncsock.py	2001/05/01 11:45:26	1.3
@@ -26,39 +26,39 @@
 SO_SYNCHRONOUS_NONALERT	= 0x20
 
 def set_sync_option (on=1):
-	result = wsock32.getsockopt (
-		INVALID_SOCKET,
-		SOCKET_SOL,
-		SO_OPENTYPE,
-		option,
-		option_len
-		)
-	if result:
-		raise SystemError, "getsockopt: (%d)" % (
-			wsock32.WSAGetLastError()
-			)
-	else:
-		old = struct.unpack ('l', option.read())[0]
-	if on:
-		new = old | SO_SYNCHRONOUS_ALERT
-	else:
-		new = old & (~SO_SYNCHRONOUS_ALERT)
-	option.write (struct.pack ('l', new))
-	result = wsock32.setsockopt (
-		INVALID_SOCKET,
-		SOCKET_SOL,
-		SO_OPENTYPE,
-		option,
-		option_len
-		)
-	if result:
-		raise SystemError, "getsockopt: (%d)" % (
-			wsock32.WSAGetLastError()
-			)
-	return old
-
+    result = wsock32.getsockopt (
+            INVALID_SOCKET,
+            SOCKET_SOL,
+            SO_OPENTYPE,
+            option,
+            option_len
+            )
+    if result:
+        raise SystemError, "getsockopt: (%d)" % (
+                wsock32.WSAGetLastError()
+                )
+    else:
+        old = struct.unpack ('l', option.read())[0]
+    if on:
+        new = old | SO_SYNCHRONOUS_ALERT
+    else:
+        new = old & (~SO_SYNCHRONOUS_ALERT)
+    option.write (struct.pack ('l', new))
+    result = wsock32.setsockopt (
+            INVALID_SOCKET,
+            SOCKET_SOL,
+            SO_OPENTYPE,
+            option,
+            option_len
+            )
+    if result:
+        raise SystemError, "getsockopt: (%d)" % (
+                wsock32.WSAGetLastError()
+                )
+    return old
+    
 def sync_on():
-	return set_sync_option (1)
-
+    return set_sync_option (1)
+    
 def sync_off():
-	return set_sync_option (0)
+    return set_sync_option (0)