/* 
   Unix SMB/CIFS implementation.
   Main winbindd server routines

   Copyright (C) Stefan Metzmacher	2005
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"
#include "events.h"
#include "system/time.h"

#define WINBINDD_DIR "/tmp/.winbindd/"
#define WINBINDD_ECHO_SOCKET  WINBINDD_DIR"echo"
#define WINBINDD_ADDR_PREFIX "127.0.255."
#define WINBINDD_ECHO_ADDR WINBINDD_ADDR_PREFIX"1"
#define WINBINDD_ECHO_PORT 55555

static void winbind_accept(struct server_connection *conn)
{
	DEBUG(10,("winbind_accept:\n"));
}

static DATA_BLOB tmp_blob;

static void winbind_recv(struct server_connection *conn, struct timeval t, uint16_t flags)
{


	NTSTATUS status;
	size_t nread;

if (!tmp_blob.data) {
	tmp_blob = data_blob_talloc(conn, NULL, 1024);
	if (tmp_blob.data == NULL) {
		return;
	}
}
	tmp_blob.length = 1024;
	status = socket_recv(conn->socket, tmp_blob.data, tmp_blob.length, &nread, 0);
	if (NT_STATUS_IS_ERR(status)) {
		DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
		talloc_free(tmp_blob.data);
		server_terminate_connection(conn, "socket_recv: failed\n");
		return;
	}
	tmp_blob.length = nread;
#if 0
DEBUG(0,("winbind_recv:\n"));
dump_data(0, tmp_blob.data, tmp_blob.length);
#endif
	conn->event.fde->flags |= EVENT_FD_WRITE;
}

static void winbind_send(struct server_connection *conn, struct timeval t, uint16_t flags)
{
	NTSTATUS status;
	size_t sendlen;

	if (tmp_blob.length > 1 && tmp_blob.data[0] == (uint8_t)'q') {

	}

	status = socket_send(conn->socket, &tmp_blob, &sendlen, 0);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
		server_terminate_connection(conn, "socket_send: failed\n");
		return;
	}

	if (tmp_blob.length > 1 && tmp_blob.data[0] == (uint8_t)'q') {
		server_terminate_connection(conn, "winbind_send: user quit\n");
		return;
	}
#if 0
DEBUG(0,("winbind_send:\n"));
dump_data(0, tmp_blob.data, tmp_blob.length);
#endif
	tmp_blob.length -= sendlen;

	if (tmp_blob.length == 0) {
		conn->event.fde->flags &= ~EVENT_FD_WRITE;
	}
}

static void winbind_idle(struct server_connection *conn, struct timeval t)
{
	DEBUG(1,("winbind_idle: not implemented!\n"));
	return;
}

static void winbind_close(struct server_connection *conn, const char *reason)
{
	DEBUG(10,("winbind_close: %s\n", reason));
}



static int winbind_task_server_contect_destructor(void *ptr)
{
	struct server_context *server = ptr;

	server_service_shutdown(server, "exit");

	return 0;	
}

static void winbind_server_task_init(struct server_task *task)
{
	const char *wb_task_service[] = { "winbind_task", NULL };
	struct server_context *server;

	DEBUG(1,("winbindsrv_task_init\n"));
	server = server_service_startup("single", wb_task_service);
	if (!server) {
		DEBUG(0,("Starting Services (winbind_task) failed.\n"));
		return;
	}

	task->task.private_data = talloc_steal(task, server);

	task->event.ctx = event_context_merge(task->event.ctx, server->event.ctx);
	server->event.ctx = talloc_reference(server, task->event.ctx);

	talloc_set_destructor(server, winbind_task_server_contect_destructor);

	/* wait for events */
	event_loop_wait(task->event.ctx);
}

static const struct server_task_ops winbind_srver_task_ops = {
	.name		= "winbind_server_task",
	.task_init	= winbind_server_task_init
};

static const struct server_task_ops *winbind_srver_task_get_ops(void)
{
	return &winbind_srver_task_ops;
}

static const struct server_stream_ops winbind_stream_ops = {
	.name			= "winbind",
	.socket_init		= NULL,
	.accept_connection	= winbind_accept,
	.recv_handler		= winbind_recv,
	.send_handler		= winbind_send,
	.idle_handler		= winbind_idle,
	.close_connection	= winbind_close
};

static const struct server_stream_ops *winbind_get_stream_ops(void)
{
	return &winbind_stream_ops;
}

static void winbind_task_init(struct server_service *service)
{
	struct server_stream_socket *stream_socket;
	uint16_t port = 1;

	DEBUG(1,("winbind_task_init\n"));

	/* Make sure the directory for NCALRPC exists */
	if (!directory_exist(WINBINDD_DIR, NULL)) {
		mkdir(WINBINDD_DIR, 0755);
	}

	stream_socket = service_setup_stream_socket(service, winbind_get_stream_ops(), "unix", WINBINDD_ECHO_SOCKET, &port);
	if (!stream_socket) {
		DEBUG(0,("service_setup_stream_socket(path=%s) failed\n",WINBINDD_ECHO_SOCKET));
		return;
	}

	port = WINBINDD_ECHO_PORT;
	stream_socket = service_setup_stream_socket(service, winbind_get_stream_ops(), "ipv4", WINBINDD_ECHO_ADDR, &port);
	if (!stream_socket) {
		DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed\n",WINBINDD_ECHO_ADDR, port));
		return;
	}

	return;
}

static const struct server_service_ops winbind_task_ops = {
	.name			= "winbind_task",
	.service_init		= winbind_task_init,
};

const struct server_service_ops *winbind_task_get_ops(void)
{
	return &winbind_task_ops;
}

static void winbind_init(struct server_service *service)
{
	DEBUG(1,("winbind_init\n"));

	server_run_task(service, winbind_srver_task_get_ops());

	return;
}

static const struct server_service_ops winbind_ops = {
	.name			= "winbind",
	.service_init		= winbind_init,
};

const struct server_service_ops *winbind_get_ops(void)
{
	return &winbind_ops;
}

NTSTATUS server_service_winbind_init(void)
{
	return NT_STATUS_OK;	
}