static PyObject *
python_proto_append_response_packet (PyObject *self, PyObject *args) {
	GString *packet;
	network_mysqld_auth_response *auth_response;

	PyObject *dict;
	if(!PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict))
		return NULL;

	packet = g_string_new(NULL);
	auth_response = network_mysqld_auth_response_new();

	PYTHON_IMPORT_INT(auth_response, capabilities);
	PYTHON_IMPORT_INT(auth_response, max_packet_size);
	PYTHON_IMPORT_INT(auth_response, charset);

	PYTHON_IMPORT_STR(auth_response, username);
	PYTHON_IMPORT_STR(auth_response, response);
	PYTHON_IMPORT_STR(auth_response, database);

	if (network_mysqld_proto_append_auth_response(packet, auth_response)) {
		network_mysqld_auth_response_free(auth_response);
		g_string_free(packet, TRUE);
		PyErr_SetString(PyExc_ValueError, "to_response_packet() failed");
		return NULL;
	}

	network_mysqld_auth_response_free(auth_response);
	PyObject *result = PyString_FromStringAndSize(S(packet));
	g_string_free(packet, TRUE);
	return result;
}
static int lua_proto_append_response_packet (lua_State *L) {
	GString *packet;
	network_mysqld_auth_response *auth_response;
	gboolean needs_auto_capabilities = TRUE;
	guint32 server_capabilities;

	luaL_checktype(L, 1, LUA_TTABLE);

	/* extract the server_capabilities first as we need them to create the struct */
	lua_getfield_literal(L, -1, C("server_capabilities"));
	if (lua_isnil(L, -1)) {
		return luaL_error(L, ".server_capabilities has to be set");
	} else if (lua_isnumber(L, -1)) {
		server_capabilities = lua_tointeger(L, -1);
	} else {
		return luaL_error(L, ".server_capabilities has to be a number");
	}
	lua_pop(L, 1);

	packet = g_string_new(NULL);	
	auth_response = network_mysqld_auth_response_new(server_capabilities);

	lua_getfield_literal(L, -1, C("capabilities"));
	if (!lua_isnil(L, -1)) {
		auth_response->client_capabilities = lua_tointeger(L, -1);
		needs_auto_capabilities = FALSE;
	}
	lua_pop(L, 1);

	LUA_IMPORT_INT(auth_response, max_packet_size);
	LUA_IMPORT_INT(auth_response, charset);

	LUA_IMPORT_STR(auth_response, username);
	LUA_IMPORT_STR_FROM(auth_response, auth_plugin_data, "response");
	LUA_IMPORT_STR(auth_response, auth_plugin_name);
	LUA_IMPORT_STR(auth_response, database);

	if (network_mysqld_proto_append_auth_response(packet, auth_response)) {
		network_mysqld_auth_response_free(auth_response);
		g_string_free(packet, TRUE);

		luaL_error(L, "to_response_packet() failed");
		g_string_free(packet, TRUE);
		return 0;
	}
	
	network_mysqld_auth_response_free(auth_response);

	lua_pushlstring(L, S(packet));
	
	g_string_free(packet, TRUE);

	return 1;
}
/**
 * @test
 *   network_mysqld_proto_get_auth_response() can decode a pre-4.0 packet
 *   network_mysqld_proto_append_auth_response() can encode the result
 *     of the network_mysqld_proto_get_auth_response() 
 */
void t_mysqld_get_auth_response_pre_41(void) {
	const char raw_packet[] = 
		"\205$"
		"\0\0\0"
		"root\0"
		;

	network_mysqld_auth_response *auth;
	network_packet packet;
	int err = 0;

	auth = network_mysqld_auth_response_new();
	packet.data = g_string_new_len(C(raw_packet));
	packet.offset = 0;

	err = err || network_mysqld_proto_get_auth_response(&packet, auth);

	g_assert_cmpint(err, ==, 0);

	g_assert(auth->username);
	g_assert_cmpint(auth->username->len, ==, 4);
	g_assert_cmpstr(auth->username->str, ==, "root");

	g_assert_cmpuint(auth->capabilities, ==,
		CLIENT_LONG_PASSWORD |
	       	CLIENT_LONG_FLAG |
		CLIENT_LOCAL_FILES | 
		CLIENT_INTERACTIVE |
		CLIENT_TRANSACTIONS 
		); 
	g_assert_cmpuint(auth->max_packet_size, ==, 0);

	g_string_truncate(packet.data, 0);
	packet.offset = 0;

	err = err || network_mysqld_proto_append_auth_response(packet.data, auth);
	g_assert_cmpint(err, ==, 0);

	g_assert_cmpint(packet.data->len, ==, sizeof(raw_packet) - 1);
	g_assert_cmpint(TRUE, ==, g_memeq(S(packet.data), raw_packet, packet.data->len));

	network_mysqld_auth_response_free(auth);

	/* empty auth struct */
	g_string_truncate(packet.data, 0);
	packet.offset = 0;

	auth = network_mysqld_auth_response_new();
	err = err || network_mysqld_proto_append_auth_response(packet.data, auth);
	g_assert_cmpint(err, ==, 0);
	network_mysqld_auth_response_free(auth);

	g_string_free(packet.data, TRUE);
}
void network_socket_free(network_socket *s) {
	if (!s) return;

	network_queue_free(s->send_queue);
	network_queue_free(s->recv_queue);
	network_queue_free(s->recv_queue_raw);

	if (s->response) network_mysqld_auth_response_free(s->response);
	if (s->challenge) network_mysqld_auth_challenge_free(s->challenge);

	network_address_free(s->dst);
	network_address_free(s->src);

	if (s->event.ev_base) { /* if .ev_base isn't set, the event never got added */
		event_del(&(s->event));
	}

	if (s->fd != -1) {
		closesocket(s->fd);
	}

    g_string_free(s->default_db, TRUE);
    g_string_free(s->charset_client, TRUE);
    g_string_free(s->charset_connection, TRUE);
    g_string_free(s->charset, TRUE);
    g_string_free(s->charset_results, TRUE);
    g_string_free(s->sql_mode, TRUE);

	g_free(s);
}
/**
 * @test
 *   network_mysqld_auth_response_new() and network_mysqld_auth_response_free() 
 *   don't cause a crash 
 */
void t_auth_response_new() {
	network_mysqld_auth_response *shake;

	shake = network_mysqld_auth_response_new();
	g_assert(shake);

	network_mysqld_auth_response_free(shake);
}
static int lua_proto_get_response_packet (lua_State *L) {
	size_t packet_len;
	const char *packet_str = luaL_checklstring(L, 1, &packet_len);
	guint32 server_capabilities = luaL_checkint(L, 2);
	network_mysqld_auth_response *auth_response;
	network_packet packet;
	GString s;
	int err = 0;

	s.str = (char *)packet_str;
	s.len = packet_len;

	packet.data = &s;
	packet.offset = 0;

	auth_response = network_mysqld_auth_response_new(server_capabilities);

	err = err || network_mysqld_proto_get_auth_response(&packet, auth_response);
	if (err) {
		network_mysqld_auth_response_free(auth_response);

		luaL_error(L, "%s: network_mysqld_proto_get_auth_response() failed", G_STRLOC);
		return 0;
	}

	lua_newtable(L);
	LUA_EXPORT_INT_TO(auth_response, client_capabilities, "capabilities");
	LUA_EXPORT_INT(auth_response, server_capabilities);
	LUA_EXPORT_INT(auth_response, max_packet_size);
	LUA_EXPORT_INT(auth_response, charset);

	LUA_EXPORT_STR(auth_response, username);
	LUA_EXPORT_STR_TO(auth_response, auth_plugin_data, "response");
	LUA_EXPORT_STR(auth_response, auth_plugin_name);
	LUA_EXPORT_STR(auth_response, database);

	network_mysqld_auth_response_free(auth_response);

	return 1;
}
void test_mysqld_auth_empty_pw(void) {
	const char raw_packet[] = 
		"&\0\0\1\205\246\3\0\0\0\0\1\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0root\0\0"
		;
	GString *packet;
	network_mysqld_auth_response *auth;

	auth = network_mysqld_auth_response_new();
	g_string_assign(auth->username, "root");
	auth->capabilities    = 
		(CLIENT_LONG_PASSWORD |
	       	CLIENT_LONG_FLAG |
		CLIENT_LOCAL_FILES | 
		CLIENT_PROTOCOL_41 |
		CLIENT_INTERACTIVE |
		CLIENT_TRANSACTIONS |
		CLIENT_SECURE_CONNECTION |
		CLIENT_MULTI_STATEMENTS |
		CLIENT_MULTI_RESULTS); 
	auth->max_packet_size = 1 << 24;
	auth->charset         = 8;
	
	packet = g_string_new(NULL);

	network_mysqld_proto_append_int8(packet, 38);
	network_mysqld_proto_append_int8(packet, 0);
	network_mysqld_proto_append_int8(packet, 0);
	network_mysqld_proto_append_int8(packet, 1);

	g_assert(0 == network_mysqld_proto_append_auth_response(packet, auth));

#if 0
	g_message("%s: packet->len = %d, packet is: %d", G_STRLOC, packet->len, sizeof(raw_packet) - 1);
#endif

	g_assert(packet->len == sizeof(raw_packet) - 1);

#if 0
	for (i = 0; i < packet->len; i++) {
		g_message("%s: [%d] %02x %c= %02x", G_STRLOC, i, packet->str[i], packet->str[i] == raw_packet[i] ? '=' : '!', raw_packet[i]);
	}
#endif

	g_assert(0 == memcmp(packet->str, raw_packet, sizeof(raw_packet) - 1));

	network_mysqld_auth_response_free(auth);

	g_string_free(packet, TRUE);
}
/**
 * @test
 *   network_mysqld_proto_get_auth_response() can decode a broken pre-4.0 packet
 */
void t_mysqld_get_auth_response_no_term(void) {
	const char raw_packet[] = 
		"\205$"
		"\0\0\0"
		"root\0" /* missing trailing \0 */
		"foo"
		;

	network_mysqld_auth_response *auth;
	network_packet packet;
	int err = 0;

	auth = network_mysqld_auth_response_new();
	packet.data = g_string_new_len(C(raw_packet));
	packet.offset = 0;

	err = err || network_mysqld_proto_get_auth_response(&packet, auth);

	g_assert_cmpint(err, !=, 0);

	network_mysqld_auth_response_free(auth);

	g_string_free(packet.data, TRUE);
}
/**
 * @test
 *   network_mysqld_proto_get_auth_response() can decode a string
 *   network_mysqld_proto_append_auth_response() can encode the result
 *     of the network_mysqld_proto_get_auth_response() 
 */
void t_mysqld_get_auth_response(void) {
	const char raw_packet[] = 
		"\205\246\3\0"
		"\0\0\0\1"
		"\10"
		"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
		"root\0"
		"\24\241\304\260>\255\1:F,\256\337K\323\340\4\273\354I\256\204"
		;

	network_mysqld_auth_response *auth;
	network_packet packet;
	int err = 0;

	auth = network_mysqld_auth_response_new();
	packet.data = g_string_new_len(C(raw_packet));
	packet.offset = 0;
	
	err = err || network_mysqld_proto_get_auth_response(&packet, auth);

	g_assert_cmpint(err, ==, 0);

	g_assert(auth->username);
	g_assert_cmpint(auth->username->len, ==, 4);
	g_assert_cmpstr(auth->username->str, ==, "root");

	g_assert_cmpuint(auth->capabilities, ==,
		CLIENT_LONG_PASSWORD |
	       	CLIENT_LONG_FLAG |
		CLIENT_LOCAL_FILES | 
		CLIENT_PROTOCOL_41 |
		CLIENT_INTERACTIVE |
		CLIENT_TRANSACTIONS |
		CLIENT_SECURE_CONNECTION |
		CLIENT_MULTI_STATEMENTS |
		CLIENT_MULTI_RESULTS); 
	g_assert_cmpuint(auth->max_packet_size, ==, 1 << 24);
	g_assert_cmpuint(auth->charset        , ==, 8);

	g_string_truncate(packet.data, 0);
	packet.offset = 0;

	err = err || network_mysqld_proto_append_auth_response(packet.data, auth);
	g_assert_cmpint(err, ==, 0);

	g_assert_cmpint(packet.data->len, ==, sizeof(raw_packet) - 1);
	g_assert_cmpint(TRUE, ==, g_memeq(S(packet.data), raw_packet, packet.data->len));

	network_mysqld_auth_response_free(auth);

	/* empty auth struct */
	g_string_truncate(packet.data, 0);
	packet.offset = 0;

	auth = network_mysqld_auth_response_new();
	err = err || network_mysqld_proto_append_auth_response(packet.data, auth);
	g_assert_cmpint(err, ==, 0);
	network_mysqld_auth_response_free(auth);

	g_string_free(packet.data, TRUE);
}
void test_mysqld_auth_with_pw(void) {
	const char raw_packet[] = 
		":\0\0\1"
		"\205\246\3\0"
		"\0\0\0\1"
		"\10"
		"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
		"root\0"
		"\24\241\304\260>\255\1:F,\256\337K\323\340\4\273\354I\256\204"
		;
	const char raw_challenge[] = 
			"%@R[SoWC"      /* part 1 */
			"+L|LG_+R={tV"; /* part 2 */

	GString *packet, *challenge, *hashed_password;
	network_mysqld_auth_response *auth;

	auth = network_mysqld_auth_response_new();
	g_string_assign(auth->username, "root");
	auth->capabilities    = 
		CLIENT_LONG_PASSWORD |
	       	CLIENT_LONG_FLAG |
		CLIENT_LOCAL_FILES | 
		CLIENT_PROTOCOL_41 |
		CLIENT_INTERACTIVE |
		CLIENT_TRANSACTIONS |
		CLIENT_SECURE_CONNECTION |
		CLIENT_MULTI_STATEMENTS |
		CLIENT_MULTI_RESULTS; 
	auth->max_packet_size = 1 << 24;
	auth->charset         = 8;

	challenge = g_string_new(NULL);
	hashed_password = g_string_new(NULL);
	g_string_append_len(challenge, raw_challenge, sizeof(raw_challenge) - 1);

	network_mysqld_proto_password_hash(hashed_password, C("123"));
	network_mysqld_proto_password_scramble(auth->response, S(challenge), S(hashed_password));

	g_string_free(hashed_password, TRUE);
	
	packet = g_string_new(NULL);

	network_mysqld_proto_append_int8(packet, 58);
	network_mysqld_proto_append_int8(packet, 0);
	network_mysqld_proto_append_int8(packet, 0);
	network_mysqld_proto_append_int8(packet, 1);

	g_assert(0 == network_mysqld_proto_append_auth_response(packet, auth));
	g_assert(packet->len == sizeof(raw_packet) - 1);

#if 0
	for (i = 0; i < packet->len; i++) {
		g_message("%s: [%d] %02x %c= %02x", G_STRLOC, i, packet->str[i], packet->str[i] == raw_packet[i] ? '=' : '!', raw_packet[i]);
	}
#endif

	g_assert(0 == memcmp(packet->str, raw_packet, sizeof(raw_packet) - 1));

	network_mysqld_auth_response_free(auth);

	g_string_free(packet, TRUE);
	g_string_free(challenge, TRUE);
}