This file is indexed.

/usr/share/pyshared/gluon/contrib/pymysql/connections.py is in python-gluon 1.99.7-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
# Python implementation of the MySQL client-server protocol
#   http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol

try:
    import hashlib
    sha_new = lambda *args, **kwargs: hashlib.new("sha1", *args, **kwargs)
except ImportError:
    import sha
    sha_new = sha.new

import socket
try:
    import ssl
    SSL_ENABLED = True
except ImportError:
    SSL_ENABLED = False

import struct
import sys
import os
import ConfigParser

try:
    import cStringIO as StringIO
except ImportError:
    import StringIO

from charset import MBLENGTH, charset_by_name, charset_by_id
from cursors import Cursor
from constants import FIELD_TYPE, FLAG
from constants import SERVER_STATUS
from constants.CLIENT import *
from constants.COMMAND import *
from util import join_bytes, byte2int, int2byte
from converters import escape_item, encoders, decoders
from err import raise_mysql_exception, Warning, Error, \
     InterfaceError, DataError, DatabaseError, OperationalError, \
     IntegrityError, InternalError, NotSupportedError, ProgrammingError

DEBUG = False

NULL_COLUMN = 251
UNSIGNED_CHAR_COLUMN = 251
UNSIGNED_SHORT_COLUMN = 252
UNSIGNED_INT24_COLUMN = 253
UNSIGNED_INT64_COLUMN = 254
UNSIGNED_CHAR_LENGTH = 1
UNSIGNED_SHORT_LENGTH = 2
UNSIGNED_INT24_LENGTH = 3
UNSIGNED_INT64_LENGTH = 8

DEFAULT_CHARSET = 'latin1'
MAX_PACKET_LENGTH = 256*256*256-1


def dump_packet(data):

    def is_ascii(data):
        if byte2int(data) >= 65 and byte2int(data) <= 122: #data.isalnum():
            return data
        return '.'
    print "packet length %d" % len(data)
    print "method call[1]: %s" % sys._getframe(1).f_code.co_name
    print "method call[2]: %s" % sys._getframe(2).f_code.co_name
    print "method call[3]: %s" % sys._getframe(3).f_code.co_name
    print "method call[4]: %s" % sys._getframe(4).f_code.co_name
    print "method call[5]: %s" % sys._getframe(5).f_code.co_name
    print "-" * 88
    dump_data = [data[i:i+16] for i in xrange(len(data)) if i%16 == 0]
    for d in dump_data:
        print ' '.join(map(lambda x:"%02X" % byte2int(x), d)) + \
                '   ' * (16 - len(d)) + ' ' * 2 + \
                ' '.join(map(lambda x:"%s" % is_ascii(x), d))
    print "-" * 88
    print ""

def _scramble(password, message):
    if password == None or len(password) == 0:
        return int2byte(0)
    if DEBUG: print 'password=' + password
    stage1 = sha_new(password).digest()
    stage2 = sha_new(stage1).digest()
    s = sha_new()
    s.update(message)
    s.update(stage2)
    result = s.digest()
    return _my_crypt(result, stage1)

def _my_crypt(message1, message2):
    length = len(message1)
    result = struct.pack('B', length)
    for i in xrange(length):
        x = (struct.unpack('B', message1[i:i+1])[0] ^ \
             struct.unpack('B', message2[i:i+1])[0])
        result += struct.pack('B', x)
    return result

# old_passwords support ported from libmysql/password.c
SCRAMBLE_LENGTH_323 = 8

class RandStruct_323(object):
    def __init__(self, seed1, seed2):
        self.max_value = 0x3FFFFFFFL
        self.seed1 = seed1 % self.max_value
        self.seed2 = seed2 % self.max_value

    def my_rnd(self):
        self.seed1 = (self.seed1 * 3L + self.seed2) % self.max_value
        self.seed2 = (self.seed1 + self.seed2 + 33L) % self.max_value
        return float(self.seed1) / float(self.max_value)

def _scramble_323(password, message):
    hash_pass = _hash_password_323(password)
    hash_message = _hash_password_323(message[:SCRAMBLE_LENGTH_323])
    hash_pass_n = struct.unpack(">LL", hash_pass)
    hash_message_n = struct.unpack(">LL", hash_message)

    rand_st = RandStruct_323(hash_pass_n[0] ^ hash_message_n[0],
                             hash_pass_n[1] ^ hash_message_n[1])
    outbuf = StringIO.StringIO()
    for _ in xrange(min(SCRAMBLE_LENGTH_323, len(message))):
        outbuf.write(int2byte(int(rand_st.my_rnd() * 31) + 64))
    extra = int2byte(int(rand_st.my_rnd() * 31))
    out = outbuf.getvalue()
    outbuf = StringIO.StringIO()
    for c in out:
        outbuf.write(int2byte(byte2int(c) ^ byte2int(extra)))
    return outbuf.getvalue()

def _hash_password_323(password):
    nr = 1345345333L
    add = 7L
    nr2 = 0x12345671L

    for c in [byte2int(x) for x in password if x not in (' ', '\t')]:
        nr^= (((nr & 63)+add)*c)+ (nr << 8) & 0xFFFFFFFF
        nr2= (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFF
        add= (add + c) & 0xFFFFFFFF

    r1 = nr & ((1L << 31) - 1L) # kill sign bits
    r2 = nr2 & ((1L << 31) - 1L)

    # pack
    return struct.pack(">LL", r1, r2)

def pack_int24(n):
    return struct.pack('BBB', n&0xFF, (n>>8)&0xFF, (n>>16)&0xFF)

def unpack_uint16(n):
  return struct.unpack('<H', n[0:2])[0]


# TODO: stop using bit-shifting in these functions...
# TODO: rename to "uint" to make it clear they're unsigned...
def unpack_int24(n):
    return struct.unpack('B',n[0])[0] + (struct.unpack('B', n[1])[0] << 8) +\
        (struct.unpack('B',n[2])[0] << 16)

def unpack_int32(n):
    return struct.unpack('B',n[0])[0] + (struct.unpack('B', n[1])[0] << 8) +\
        (struct.unpack('B',n[2])[0] << 16) + (struct.unpack('B', n[3])[0] << 24)

def unpack_int64(n):
    return struct.unpack('B',n[0])[0] + (struct.unpack('B', n[1])[0]<<8) +\
    (struct.unpack('B',n[2])[0] << 16) + (struct.unpack('B',n[3])[0]<<24)+\
    (struct.unpack('B',n[4])[0] << 32) + (struct.unpack('B',n[5])[0]<<40)+\
    (struct.unpack('B',n[6])[0] << 48) + (struct.unpack('B',n[7])[0]<<56)

def defaulterrorhandler(connection, cursor, errorclass, errorvalue):
    err = errorclass, errorvalue
    if DEBUG:
        raise

    if cursor:
        cursor.messages.append(err)
    else:
        connection.messages.append(err)
    del cursor
    del connection

    if not issubclass(errorclass, Error):
        raise Error(errorclass, errorvalue)
    else:
        raise errorclass, errorvalue


class MysqlPacket(object):
  """Representation of a MySQL response packet.  Reads in the packet
  from the network socket, removes packet header and provides an interface
  for reading/parsing the packet results."""

  def __init__(self, socket):
    self.__position = 0
    self.__recv_packet(socket)
    del socket

  def __recv_packet(self, socket):
    """Parse the packet header and read entire packet payload into buffer."""
    packet_header = socket.recv(4)
    while len(packet_header) < 4:
        d = socket.recv(4 - len(packet_header))
        if len(d) == 0:
            raise OperationalError(2013, "Lost connection to MySQL server during query")
        packet_header += d

    if DEBUG: dump_packet(packet_header)
    packet_length_bin = packet_header[:3]
    self.__packet_number = byte2int(packet_header[3])
    # TODO: check packet_num is correct (+1 from last packet)

    bin_length = packet_length_bin + int2byte(0)  # pad little-endian number
    bytes_to_read = struct.unpack('<I', bin_length)[0]

    payload_buff = []  # this is faster than cStringIO
    while bytes_to_read > 0:
      recv_data = socket.recv(bytes_to_read)
      if len(recv_data) == 0:
            raise OperationalError(2013, "Lost connection to MySQL server during query")
      if DEBUG: dump_packet(recv_data)
      payload_buff.append(recv_data)
      bytes_to_read -= len(recv_data)
    self.__data = join_bytes(payload_buff)

  def packet_number(self): return self.__packet_number

  def get_all_data(self): return self.__data

  def read(self, size):
    """Read the first 'size' bytes in packet and advance cursor past them."""
    result = self.peek(size)
    self.advance(size)
    return result

  def read_all(self):
    """Read all remaining data in the packet.

    (Subsequent read() or peek() will return errors.)
    """
    result = self.__data[self.__position:]
    self.__position = None  # ensure no subsequent read() or peek()
    return result

  def advance(self, length):
    """Advance the cursor in data buffer 'length' bytes."""
    new_position = self.__position + length
    if new_position < 0 or new_position > len(self.__data):
      raise Exception('Invalid advance amount (%s) for cursor.  '
                      'Position=%s' % (length, new_position))
    self.__position = new_position

  def rewind(self, position=0):
    """Set the position of the data buffer cursor to 'position'."""
    if position < 0 or position > len(self.__data):
      raise Exception("Invalid position to rewind cursor to: %s." % position)
    self.__position = position

  def peek(self, size):
    """Look at the first 'size' bytes in packet without moving cursor."""
    result = self.__data[self.__position:(self.__position+size)]
    if len(result) != size:
      error = ('Result length not requested length:\n'
               'Expected=%s.  Actual=%s.  Position: %s.  Data Length: %s'
               % (size, len(result), self.__position, len(self.__data)))
      if DEBUG:
        print error
        self.dump()
      raise AssertionError(error)
    return result

  def get_bytes(self, position, length=1):
    """Get 'length' bytes starting at 'position'.

    Position is start of payload (first four packet header bytes are not
    included) starting at index '0'.

    No error checking is done.  If requesting outside end of buffer
    an empty string (or string shorter than 'length') may be returned!
    """
    return self.__data[position:(position+length)]

  def read_length_coded_binary(self):
    """Read a 'Length Coded Binary' number from the data buffer.

    Length coded numbers can be anywhere from 1 to 9 bytes depending
    on the value of the first byte.
    """
    c = byte2int(self.read(1))
    if c == NULL_COLUMN:
      return None
    if c < UNSIGNED_CHAR_COLUMN:
      return c
    elif c == UNSIGNED_SHORT_COLUMN:
      return unpack_uint16(self.read(UNSIGNED_SHORT_LENGTH))
    elif c == UNSIGNED_INT24_COLUMN:
      return unpack_int24(self.read(UNSIGNED_INT24_LENGTH))
    elif c == UNSIGNED_INT64_COLUMN:
      # TODO: what was 'longlong'?  confirm it wasn't used?
      return unpack_int64(self.read(UNSIGNED_INT64_LENGTH))

  def read_length_coded_string(self):
    """Read a 'Length Coded String' from the data buffer.

    A 'Length Coded String' consists first of a length coded
    (unsigned, positive) integer represented in 1-9 bytes followed by
    that many bytes of binary data.  (For example "cat" would be "3cat".)
    """
    length = self.read_length_coded_binary()
    if length is None:
        return None
    return self.read(length)

  def is_ok_packet(self):
    return byte2int(self.get_bytes(0)) == 0

  def is_eof_packet(self):
    return byte2int(self.get_bytes(0)) == 254  # 'fe'

  def is_resultset_packet(self):
    field_count = byte2int(self.get_bytes(0))
    return field_count >= 1 and field_count <= 250

  def is_error_packet(self):
    return byte2int(self.get_bytes(0)) == 255

  def check_error(self):
    if self.is_error_packet():
      self.rewind()
      self.advance(1)  # field_count == error (we already know that)
      errno = unpack_uint16(self.read(2))
      if DEBUG: print "errno = %d" % errno
      raise_mysql_exception(self.__data)

  def dump(self):
    dump_packet(self.__data)


class FieldDescriptorPacket(MysqlPacket):
  """A MysqlPacket that represents a specific column's metadata in the result.

  Parsing is automatically done and the results are exported via public
  attributes on the class such as: db, table_name, name, length, type_code.
  """

  def __init__(self, *args):
    MysqlPacket.__init__(self, *args)
    self.__parse_field_descriptor()

  def __parse_field_descriptor(self):
    """Parse the 'Field Descriptor' (Metadata) packet.

    This is compatible with MySQL 4.1+ (not compatible with MySQL 4.0).
    """
    self.catalog = self.read_length_coded_string()
    self.db = self.read_length_coded_string()
    self.table_name = self.read_length_coded_string()
    self.org_table = self.read_length_coded_string()
    self.name = self.read_length_coded_string()
    self.org_name = self.read_length_coded_string()
    self.advance(1)  # non-null filler
    self.charsetnr = struct.unpack('<H', self.read(2))[0]
    self.length = struct.unpack('<I', self.read(4))[0]
    self.type_code = byte2int(self.read(1))
    self.flags = struct.unpack('<H', self.read(2))[0]
    self.scale = byte2int(self.read(1))  # "decimals"
    self.advance(2)  # filler (always 0x00)

    # 'default' is a length coded binary and is still in the buffer?
    # not used for normal result sets...

  def description(self):
    """Provides a 7-item tuple compatible with the Python PEP249 DB Spec."""
    desc = []
    desc.append(self.name)
    desc.append(self.type_code)
    desc.append(None) # TODO: display_length; should this be self.length?
    desc.append(self.get_column_length()) # 'internal_size'
    desc.append(self.get_column_length()) # 'precision'  # TODO: why!?!?
    desc.append(self.scale)

    # 'null_ok' -- can this be True/False rather than 1/0?
    #              if so just do:  desc.append(bool(self.flags % 2 == 0))
    if self.flags % 2 == 0:
      desc.append(1)
    else:
      desc.append(0)
    return tuple(desc)

  def get_column_length(self):
    if self.type_code == FIELD_TYPE.VAR_STRING:
      mblen = MBLENGTH.get(self.charsetnr, 1)
      return self.length // mblen
    return self.length

  def __str__(self):
    return ('%s %s.%s.%s, type=%s'
            % (self.__class__, self.db, self.table_name, self.name,
               self.type_code))


class Connection(object):
    """
    Representation of a socket with a mysql server.

    The proper way to get an instance of this class is to call
    connect()."""
    errorhandler = defaulterrorhandler

    def __init__(self, host="localhost", user=None, passwd="",
                 db=None, port=3306, unix_socket=None,
                 charset='', sql_mode=None,
                 read_default_file=None, conv=decoders, use_unicode=None,
                 client_flag=0, cursorclass=Cursor, init_command=None,
                 connect_timeout=None, ssl=None, read_default_group=None,
                 compress=None, named_pipe=None):
        """
        Establish a connection to the MySQL database. Accepts several
        arguments:

        host: Host where the database server is located
        user: Username to log in as
        passwd: Password to use.
        db: Database to use, None to not use a particular one.
        port: MySQL port to use, default is usually OK.
        unix_socket: Optionally, you can use a unix socket rather than TCP/IP.
        charset: Charset you want to use.
        sql_mode: Default SQL_MODE to use.
        read_default_file: Specifies  my.cnf file to read these parameters from under the [client] section.
        conv: Decoders dictionary to use instead of the default one. This is used to provide custom marshalling of types. See converters.
        use_unicode: Whether or not to default to unicode strings. This option defaults to true for Py3k.
        client_flag: Custom flags to send to MySQL. Find potential values in constants.CLIENT.
        cursorclass: Custom cursor class to use.
        init_command: Initial SQL statement to run when connection is established.
        connect_timeout: Timeout before throwing an exception when connecting.
        ssl: A dict of arguments similar to mysql_ssl_set()'s parameters. For now the capath and cipher arguments are not supported.
        read_default_group: Group to read from in the configuration file.
        compress; Not supported
        named_pipe: Not supported
        """

        if use_unicode is None and sys.version_info[0] > 2:
            use_unicode = True

        if compress or named_pipe:
            raise NotImplementedError, "compress and named_pipe arguments are not supported"

        if ssl and (ssl.has_key('capath') or ssl.has_key('cipher')):
            raise NotImplementedError, 'ssl options capath and cipher are not supported'

        self.ssl = False
        if ssl:
            if not SSL_ENABLED:
                raise NotImplementedError, "ssl module not found"
            self.ssl = True
            client_flag |= SSL
            for k in ('key', 'cert', 'ca'):
                v = None
                if ssl.has_key(k):
                    v = ssl[k]
                setattr(self, k, v)

        if read_default_group and not read_default_file:
            if sys.platform.startswith("win"):
                read_default_file = "c:\\my.ini"
            else:
                read_default_file = "/etc/my.cnf"

        if read_default_file:
            if not read_default_group:
                read_default_group = "client"

            cfg = ConfigParser.RawConfigParser()
            cfg.read(os.path.expanduser(read_default_file))

            def _config(key, default):
                try:
                    return cfg.get(read_default_group,key)
                except:
                    return default

            user = _config("user",user)
            passwd = _config("password",passwd)
            host = _config("host", host)
            db = _config("db",db)
            unix_socket = _config("socket",unix_socket)
            port = _config("port", port)
            charset = _config("default-character-set", charset)

        self.host = host
        self.port = port
        self.user = user
        self.password = passwd
        self.db = db
        self.unix_socket = unix_socket
        if charset:
            self.charset = charset
            self.use_unicode = True
        else:
            self.charset = DEFAULT_CHARSET
            self.use_unicode = False

        if use_unicode:
            self.use_unicode = use_unicode

        client_flag |= CAPABILITIES
        client_flag |= MULTI_STATEMENTS
        if self.db:
            client_flag |= CONNECT_WITH_DB
        self.client_flag = client_flag

        self.cursorclass = cursorclass
        self.connect_timeout = connect_timeout

        self._connect()

        self.messages = []
        self.set_charset(charset)
        self.encoders = encoders
        self.decoders = conv

        self._affected_rows = 0
        self.host_info = "Not connected"

        self.autocommit(False)

        if sql_mode is not None:
            c = self.cursor()
            c.execute("SET sql_mode=%s", (sql_mode,))

        self.commit()

        if init_command is not None:
            c = self.cursor()
            c.execute(init_command)

            self.commit()


    def close(self):
        ''' Send the quit message and close the socket '''
        send_data = struct.pack('<i',1) + int2byte(COM_QUIT)
        self.socket.send(send_data)
        self.socket.close()
        self.socket = None

    def autocommit(self, value):
        ''' Set whether or not to commit after every execute() '''
        try:
            self._execute_command(COM_QUERY, "SET AUTOCOMMIT = %s" % \
                                      self.escape(value))
            self.read_packet()
        except:
            exc,value,tb = sys.exc_info()
            self.errorhandler(None, exc, value)

    def commit(self):
        ''' Commit changes to stable storage '''
        try:
            self._execute_command(COM_QUERY, "COMMIT")
            self.read_packet()
        except:
            exc,value,tb = sys.exc_info()
            self.errorhandler(None, exc, value)

    def rollback(self):
        ''' Roll back the current transaction '''
        try:
            self._execute_command(COM_QUERY, "ROLLBACK")
            self.read_packet()
        except:
            exc,value,tb = sys.exc_info()
            self.errorhandler(None, exc, value)

    def escape(self, obj):
        ''' Escape whatever value you pass to it  '''
        return escape_item(obj, self.charset)

    def literal(self, obj):
        ''' Alias for escape() '''
        return escape_item(obj, self.charset)

    def cursor(self):
        ''' Create a new cursor to execute queries with '''
        return self.cursorclass(self)

    def __enter__(self):
        ''' Context manager that returns a Cursor '''
        return self.cursor()

    def __exit__(self, exc, value, traceback):
        ''' On successful exit, commit. On exception, rollback. '''
        if exc:
            self.rollback()
        else:
            self.commit()

    # The following methods are INTERNAL USE ONLY (called from Cursor)
    def query(self, sql):
        self._execute_command(COM_QUERY, sql)
        self._affected_rows = self._read_query_result()
        return self._affected_rows

    def next_result(self):
        self._affected_rows = self._read_query_result()
        return self._affected_rows

    def affected_rows(self):
        return self._affected_rows

    def kill(self, thread_id):
        arg = struct.pack('<I', thread_id)
        try:
            self._execute_command(COM_PROCESS_KILL, arg)
        except:
            exc,value,tb = sys.exc_info()
            self.errorhandler(None, exc, value)
            return
        pkt = self.read_packet()
        return pkt.is_ok_packet()

    def ping(self, reconnect=True):
        ''' Check if the server is alive '''
        try:
            self._execute_command(COM_PING, "")
        except:
            if reconnect:
                self._connect()
                return self.ping(False)
            else:
                exc,value,tb = sys.exc_info()
                self.errorhandler(None, exc, value)
                return

        pkt = self.read_packet()
        return pkt.is_ok_packet()

    def set_charset(self, charset):
        try:
            if charset:
                self._execute_command(COM_QUERY, "SET NAMES %s" %
                                      self.escape(charset))
                self.read_packet()
                self.charset = charset
        except:
            exc,value,tb = sys.exc_info()
            self.errorhandler(None, exc, value)

    def _connect(self):
        try:
            if self.unix_socket and (self.host == 'localhost' or self.host == '127.0.0.1'):
                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                t = sock.gettimeout()
                sock.settimeout(self.connect_timeout)
                sock.connect(self.unix_socket)
                sock.settimeout(t)
                self.host_info = "Localhost via UNIX socket"
                if DEBUG: print 'connected using unix_socket'
            else:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                t = sock.gettimeout()
                sock.settimeout(self.connect_timeout)
                sock.connect((self.host, self.port))
                sock.settimeout(t)
                self.host_info = "socket %s:%d" % (self.host, self.port)
                if DEBUG: print 'connected using socket'
            self.socket = sock
            self._get_server_information()
            self._request_authentication()
        except socket.error, e:
            raise OperationalError(2003, "Can't connect to MySQL server on %r (%d)" % (self.host, e.args[0]))

    def read_packet(self, packet_type=MysqlPacket):
      """Read an entire "mysql packet" in its entirety from the network
      and return a MysqlPacket type that represents the results."""

      # TODO: is socket.recv(small_number) significantly slower than
      #       socket.recv(large_number)?  if so, maybe we should buffer
      #       the socket.recv() (though that obviously makes memory management
      #       more complicated.
      packet = packet_type(self.socket)
      packet.check_error()
      return packet

    def _read_query_result(self):
        result = MySQLResult(self)
        result.read()
        self._result = result
        return result.affected_rows

    def _send_command(self, command, sql):
        #send_data = struct.pack('<i', len(sql) + 1) + command + sql
        # could probably be more efficient, at least it's correct
        if not self.socket:
            self.errorhandler(None, InterfaceError, "(0, '')")

        if isinstance(sql, unicode):
            sql = sql.encode(self.charset)

        buf = int2byte(command) + sql
        pckt_no = 0
        while len(buf) >= MAX_PACKET_LENGTH:
            header = struct.pack('<i', MAX_PACKET_LENGTH)[:-1]+int2byte(pckt_no)
            send_data = header + buf[:MAX_PACKET_LENGTH]
            self.socket.send(send_data)
            if DEBUG: dump_packet(send_data)
            buf = buf[MAX_PACKET_LENGTH:]
            pckt_no += 1
        header = struct.pack('<i', len(buf))[:-1]+int2byte(pckt_no)
        self.socket.send(header+buf)


        #sock = self.socket
        #sock.send(send_data)

        #

    def _execute_command(self, command, sql):
        self._send_command(command, sql)

    def _request_authentication(self):
        self._send_authentication()

    def _send_authentication(self):
        sock = self.socket
        self.client_flag |= CAPABILITIES
        if self.server_version.startswith('5'):
            self.client_flag |= MULTI_RESULTS

        if self.user is None:
            raise ValueError, "Did not specify a username"

        charset_id = charset_by_name(self.charset).id
        self.user = self.user.encode(self.charset)

        data_init = struct.pack('<i', self.client_flag) + struct.pack("<I", 1) + \
                     int2byte(charset_id) + int2byte(0)*23

        next_packet = 1

        if self.ssl:
            data = pack_int24(len(data_init)) + int2byte(next_packet) + data_init
            next_packet += 1

            if DEBUG: dump_packet(data)

            sock.send(data)
            sock = self.socket = ssl.wrap_socket(sock, keyfile=self.key,
                                           certfile=self.cert,
                                           ssl_version=ssl.PROTOCOL_TLSv1,
                                           cert_reqs=ssl.CERT_REQUIRED,
                                           ca_certs=self.ca)

        data = data_init + self.user+int2byte(0) + _scramble(self.password.encode(self.charset), self.salt)

        if self.db:
            self.db = self.db.encode(self.charset)
            data += self.db + int2byte(0)

        data = pack_int24(len(data)) + int2byte(next_packet) + data
        next_packet += 2

        if DEBUG: dump_packet(data)

        sock.send(data)

        auth_packet = MysqlPacket(sock)
        auth_packet.check_error()
        if DEBUG: auth_packet.dump()

        # if old_passwords is enabled the packet will be 1 byte long and
        # have the octet 254

        if auth_packet.is_eof_packet():
            # send legacy handshake
            #raise NotImplementedError, "old_passwords are not supported. Check to see if mysqld was started with --old-passwords, if old-passwords=1 in a my.cnf file, or if there are some short hashes in your mysql.user table."
            # TODO: is this the correct charset?
            data = _scramble_323(self.password.encode(self.charset), self.salt.encode(self.charset)) + int2byte(0)
            data = pack_int24(len(data)) + int2byte(next_packet) + data

            sock.send(data)
            auth_packet = MysqlPacket(sock)
            auth_packet.check_error()
            if DEBUG: auth_packet.dump()


    # _mysql support
    def thread_id(self):
        return self.server_thread_id[0]

    def character_set_name(self):
        return self.charset

    def get_host_info(self):
        return self.host_info

    def get_proto_info(self):
        return self.protocol_version

    def _get_server_information(self):
        sock = self.socket
        i = 0
        packet = MysqlPacket(sock)
        data = packet.get_all_data()

        if DEBUG: dump_packet(data)
        #packet_len = byte2int(data[i:i+1])
        #i += 4
        self.protocol_version = byte2int(data[i:i+1])

        i += 1
        server_end = data.find(int2byte(0), i)
        # TODO: is this the correct charset? should it be default_charset?
        self.server_version = data[i:server_end].decode(self.charset)

        i = server_end + 1
        self.server_thread_id = struct.unpack('<h', data[i:i+2])

        i += 4
        self.salt = data[i:i+8]

        i += 9
        if len(data) >= i + 1:
            i += 1

        self.server_capabilities = struct.unpack('<h', data[i:i+2])[0]

        i += 1
        self.server_language = byte2int(data[i:i+1])
        self.server_charset = charset_by_id(self.server_language).name

        i += 16
        if len(data) >= i+12-1:
            rest_salt = data[i:i+12]
            self.salt += rest_salt

    def get_server_info(self):
        return self.server_version

    Warning = Warning
    Error = Error
    InterfaceError = InterfaceError
    DatabaseError = DatabaseError
    DataError = DataError
    OperationalError = OperationalError
    IntegrityError = IntegrityError
    InternalError = InternalError
    ProgrammingError = ProgrammingError
    NotSupportedError = NotSupportedError

# TODO: move OK and EOF packet parsing/logic into a proper subclass
#       of MysqlPacket like has been done with FieldDescriptorPacket.
class MySQLResult(object):

    def __init__(self, connection):
        from weakref import proxy
        self.connection = proxy(connection)
        self.affected_rows = None
        self.insert_id = None
        self.server_status = 0
        self.warning_count = 0
        self.message = None
        self.field_count = 0
        self.description = None
        self.rows = None
        self.has_next = None

    def read(self):
        self.first_packet = self.connection.read_packet()

        # TODO: use classes for different packet types?
        if self.first_packet.is_ok_packet():
            self._read_ok_packet()
        else:
            self._read_result_packet()

    def _read_ok_packet(self):
        self.first_packet.advance(1)  # field_count (always '0')
        self.affected_rows = self.first_packet.read_length_coded_binary()
        self.insert_id = self.first_packet.read_length_coded_binary()
        self.server_status = struct.unpack('<H', self.first_packet.read(2))[0]
        self.warning_count = struct.unpack('<H', self.first_packet.read(2))[0]
        self.message = self.first_packet.read_all()

    def _read_result_packet(self):
        self.field_count = byte2int(self.first_packet.read(1))
        self._get_descriptions()
        self._read_rowdata_packet()

    # TODO: implement this as an iteratable so that it is more
    #       memory efficient and lower-latency to client...
    def _read_rowdata_packet(self):
      """Read a rowdata packet for each data row in the result set."""
      rows = []
      while True:
        packet = self.connection.read_packet()
        if packet.is_eof_packet():
            self.warning_count = packet.read(2)
            server_status = struct.unpack('<h', packet.read(2))[0]
            self.has_next = (server_status
                             & SERVER_STATUS.SERVER_MORE_RESULTS_EXISTS)
            break

        row = []
        for field in self.fields:
            if field.type_code in self.connection.decoders:
                converter = self.connection.decoders[field.type_code]

                if DEBUG: print "DEBUG: field=%s, converter=%s" % (field, converter)
                data = packet.read_length_coded_string()
                converted = None
                if data != None:
                    converted = converter(self.connection, field, data)

            row.append(converted)

        rows.append(tuple(row))

      self.affected_rows = len(rows)
      self.rows = tuple(rows)
      if DEBUG: self.rows

    def _get_descriptions(self):
        """Read a column descriptor packet for each column in the result."""
        self.fields = []
        description = []
        for i in xrange(self.field_count):
            field = self.connection.read_packet(FieldDescriptorPacket)
            self.fields.append(field)
            description.append(field.description())

        eof_packet = self.connection.read_packet()
        assert eof_packet.is_eof_packet(), 'Protocol error, expecting EOF'
        self.description = tuple(description)