<commit_before>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 <NAME>. All rights reserved.
// http://www.julienschmidt.com
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"bytes"
	"database/sql/driver"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math"
	"time"
)

// Packets documentation:
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html

// Read packet to buffer 'data'
func (mc *mysqlConn) readPacket() (data []byte, err error) {
	// Read packet header
	data, err = mc.buf.readNext(4)
	if err != nil {
		errLog.Print(err.Error())
		return nil, driver.ErrBadConn
	}

	// Packet Length [24 bit]
	pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)

	if pktLen < 1 {
		errLog.Print(errMalformPkt.Error())
		return nil, driver.ErrBadConn
	}

	// Check Packet Sync [8 bit]
	if data[3] != mc.sequence {
		if data[3] > mc.sequence {
			return nil, errPktSyncMul
		} else {
			return nil, errPktSync
		}
	}
	mc.sequence++

	// Read packet body [pktLen bytes]
	data, err = mc.buf.readNext(pktLen)
	if err == nil {
		if pktLen < maxPacketSize {
			return data, nil
		}

		// More data
		var data2 []byte
		data2, err = mc.readPacket()
		if err == nil {
			return append(data, data2...), nil
		}
	}
	errLog.Print(err.Error())
	return nil, driver.ErrBadConn
}

// Write packet buffer 'data'
// The packet header must be already included
func (mc *mysqlConn) writePacket(data []byte) error {
	if len(data)-4 <= mc.maxWriteSize { // Can send data at once
		// Write packet
		n, err := mc.netConn.Write(data)
		if err == nil && n == len(data) {
			mc.sequence++
			return nil
		}

		// Handle error
		if err == nil { // n != len(data)
			errLog.Print(errMalformPkt.Error())
		} else {
			errLog.Print(err.Error())
		}
		return driver.ErrBadConn
	}

	// Must split packet
	return mc.splitPacket(data)
}

func (mc *mysqlConn) splitPacket(data []byte) (err error) {
	pktLen := len(data) - 4

	if pktLen > mc.maxPacketAllowed {
		return errPktTooLarge
	}

	for pktLen >= maxPacketSize {
		data[0] = 0xff
		data[1] = 0xff
		data[2] = 0xff
		data[3] = mc.sequence

		// Write packet
		n, err := mc.netConn.Write(data[:4+maxPacketSize])
		if err == nil && n == 4+maxPacketSize {
			mc.sequence++
			data = data[maxPacketSize:]
			pktLen -= maxPacketSize
			continue
		}

		// Handle error
		if err == nil { // n != len(data)
			errLog.Print(errMalformPkt.Error())
		} else {
			errLog.Print(err.Error())
		}
		return driver.ErrBadConn
	}

	data[0] = byte(pktLen)
	data[1] = byte(pktLen >> 8)
	data[2] = byte(pktLen >> 16)
	data[3] = mc.sequence
	return mc.writePacket(data)
}

/******************************************************************************
*                           Initialisation Process                            *
******************************************************************************/

// Handshake Initialization Packet
// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::Handshake
func (mc *mysqlConn) readInitPacket() (err error) {
	data, err := mc.readPacket()
	if err != nil {
		return
	}

	if data[0] == iERR {
		return mc.handleErrorPacket(data)
	}

	// protocol version [1 byte]
	if data[0] < minProtocolVersion {
		err = fmt.Errorf(
			"Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required",
			data[0],
			minProtocolVersion)
	}

	// server version [null terminated string]
	// connection id [4 bytes]
	pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4

	// first part of the password cipher [8 bytes]
	mc.cipher = append(mc.cipher, data[pos:pos+8]...)

	// (filler) always 0x00 [1 byte]
	pos += 8 + 1

	// capability flags (lower 2 bytes) [2 bytes]
	mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
	if mc.flags&clientProtocol41 == 0 {
		err = errors.New("MySQL-Server does not support required Protocol 41+")
	}
	pos += 2

	if len(data) > pos {
		// character set [1 byte]
		mc.charset = data[pos]

		// status flags [2 bytes]
		// capability flags (upper 2 bytes) [2 bytes]
		// length of auth-plugin-data [1 byte]
		// reserved (all [00]) [10 bytes]
		pos += 1 + 2 + 2 + 1 + 10

		// second part of the password cipher [12? bytes]
		// The documentation is ambiguous about the length.
		// The official Python library uses the fixed length 12
		// which is not documented but seems to work.
		mc.cipher = append(mc.cipher, data[pos:pos+12]...)

		// TODO: Verify string termination
		// EOF for version >= (5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2)
		// \NUL otherwise
		//if data[len(data)-1] == 0 {
		//	return
		//}
		//return errMalformPkt
	}

	return
}

// Client Authentication Packet
// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::HandshakeResponse
func (mc *mysqlConn) writeAuthPacket() error {<commit_msg>todo fmt<commit_after>// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2012 <NAME>. All rights reserved.
// http://www.julienschmidt.com
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package mysql

import (
	"bytes"
	"database/sql/driver"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math"
	"time"
)

// Packets documentation:
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html

// Read packet to buffer 'data'
func (mc *mysqlConn) readPacket() (data []byte, err error) {
	// Read packet header
	data, err = mc.buf.readNext(4)
	if err != nil {
		errLog.Print(err.Error())
		return nil, driver.ErrBadConn
	}

	// Packet Length [24 bit]
	pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)

	if pktLen < 1 {
		errLog.Print(errMalformPkt.Error())
		return nil, driver.ErrBadConn
	}

	// Check Packet Sync [8 bit]
	if data[3] != mc.sequence {
		if data[3] > mc.sequence {
			return nil, errPktSyncMul
		} else {
			return nil, errPktSync
		}
	}
	mc.sequence++

	// Read packet body [pktLen bytes]
	data, err = mc.buf.readNext(pktLen)
	if err == nil {
		if pktLen < maxPacketSize {
			return data, nil
		}

		// More data
		var data2 []byte
		data2, err = mc.readPacket()
		if err == nil {
			return append(data, data2...), nil
		}
	}
	errLog.Print(err.Error())
	return nil, driver.ErrBadConn
}

// Write packet buffer 'data'
// The packet header must be already included
func (mc *mysqlConn) writePacket(data []byte) error {
	if len(data)-4 <= mc.maxWriteSize { // Can send data at once
		// Write packet
		n, err := mc.netConn.Write(data)
		if err == nil && n == len(data) {
			mc.sequence++
			return nil
		}

		// Handle error
		if err == nil { // n != len(data)
			errLog.Print(errMalformPkt.Error())
		} else {
			errLog.Print(err.Error())
		}
		return driver.ErrBadConn
	}

	// Must split packet
	return mc.splitPacket(data)
}

func (mc *mysqlConn) splitPacket(data []byte) (err error) {
	pktLen := len(data) - 4

	if pktLen > mc.maxPacketAllowed {
		return errPktTooLarge
	}

	for pktLen >= maxPacketSize {
		data[0] = 0xff
		data[1] = 0xff
		data[2] = 0xff
		data[3] = mc.sequence

		// Write packet
		n, err := mc.netConn.Write(data[:4+maxPacketSize])
		if err == nil && n == 4+maxPacketSize {
			mc.sequence++
			data = data[maxPacketSize:]
			pktLen -= maxPacketSize
			continue
		}

		// Handle error
		if err == nil { // n != len(data)
			errLog.Print(errMalformPkt.Error())
		} else {
			errLog.Print(err.Error())
		}
		return driver.ErrBadConn
	}

	data[0] = byte(pktLen)
	data[1] = byte(pktLen >> 8)
	data[2] = byte(pktLen >> 16)
	data[3] = mc.sequence
	return mc.writePacket(data)
}

/******************************************************************************
*                           Initialisation Process                            *
******************************************************************************/

// Handshake Initialization Packet
// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::Handshake
func (mc *mysqlConn) readInitPacket() (err error) {
	data, err := mc.readPacket()
	if err != nil {
		return
	}

	if data[0] == iERR {
		return mc.handleErrorPacket(data)
	}

	// protocol version [1 byte]
	if data[0] < minProtocolVersion {
		err = fmt.Errorf(
			"Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required",
			data[0],
			minProtocolVersion)
	}

	// server version [null terminated string]
	// connection id [4 bytes]
	pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4

	// first part of the password cipher [8 bytes]
	mc.cipher = append(mc.cipher, data[pos:pos+8]...)

	// (filler) always 0x00 [1 byte]
	pos += 8 + 1

	// capability flags (lower 2 bytes) [2 bytes]
	mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
	if mc.flags&clientProtocol41 == 0 {
		err = errors.New("MySQL-Server does not support required Protocol 41+")
	}
	pos += 2

	if len(data) > pos {
		// character set [1 byte]
		mc.charset = data[pos]

		// status flags [2 bytes]
		// capability flags (upper 2 bytes) [2 bytes]
		// length of auth-plugin-data [1 byte]
		// reserved (all [00]) [10 bytes]
		pos += 1 + 2 + 2 + 1 + 10

		// second part of the password cipher [12? bytes]
		// The documentation is ambiguous about the length.
		// The official Python library uses the fixed length 12
		// which is not documented but seems to work.
		mc.cipher = append(mc.cipher, data[pos:pos+12]...)

		// TODO: Verify string termination
		// EOF if version (>= 5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2)
		// \NUL otherwise
		// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::Handshake
		//
		//if data[len(data)-1] == 0 {
		//	return
		//}
		//return errMalformPkt
	}

	return
}

// Client Authentication Packet
// http://dev.mysql.com/doc/internals/en/connection-phase.html#packet-Protocol::HandshakeResponse
func (mc *mysqlConn) writeAuthPacket() error {
{'fraction_non_alphanumeric': 0.17425214729983216, 'fraction_numerical': 0.022707078684963966, 'mean_word_length': 3.0715434083601285, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4907319', 'n_tokens_mistral': 3893, 'n_tokens_neox': 3521, 'n_words': 980}