/*-
 * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/param.h>
#include <nss.h>
#include <stdlib.h>
#include <errno.h>
#include <ldap.h>
#include <group_defs.h>
#include <passwd_defs.h>
#include <stdlib.h>
#include <string.h>

#include "bsdnss.h"

extern enum nss_status 
_nss_ldap_getgrent_r(struct group *, char *, size_t,
	int *);
extern enum nss_status 
_nss_ldap_getgrnam_r(const char *, struct group *,
	char *, size_t, int *);
extern enum nss_status 
_nss_ldap_getgrgid_r(gid_t gid, struct group *, char *,
	size_t, int *);
extern enum nss_status _nss_ldap_setgrent(void);
extern enum nss_status _nss_ldap_endgrent(void);

extern enum nss_status 
_nss_ldap_getpwent_r(struct passwd *, char *, size_t,
	int *);
extern enum nss_status 
_nss_ldap_getpwnam_r(const char *, struct passwd *,
	char *, size_t, int *);
extern enum nss_status 
_nss_ldap_getpwuid_r(gid_t gid, struct passwd *, char *,
	size_t, int *);
extern enum nss_status _nss_ldap_setpwent(void);
extern enum nss_status _nss_ldap_endpwent(void);

extern void lookupd_set_ldap_euid(uid_t new_uid);

#define GROUP_BUFFER_ALLOC_STEP 512
#define GROUP_BUFFER_MAX_SIZE 8192

#define PASSWD_BUFFER_ALLOC_STEP 512
#define PASSWD_BUFFER_MAX_SIZE 8192

void _util_init_tss_keys();
void _resolve_init_tss_keys();
void _ldap_pwd_init_tss_keys();
void _ldap_nss_init_tss_keys();
void _ldap_grp_init_tss_keys();

void 
_init()
{
	_util_init_tss_keys();
	_resolve_init_tss_keys();
	_ldap_pwd_init_tss_keys();
	_ldap_nss_init_tss_keys();
	_ldap_grp_init_tss_keys();
}

enum nss_status 
ldap_getgrnam(struct getgrnam_data * request_data, struct group_result * result_data, int *error)
{
	enum nss_status status_code = NS_UNAVAIL;

	result_data->grp_buffer_size = GROUP_BUFFER_ALLOC_STEP;

	do {
		if (result_data->grp_buffer_size != GROUP_BUFFER_ALLOC_STEP) {
			free(result_data->grp_buffer);
			result_data->grp_buffer_size += GROUP_BUFFER_ALLOC_STEP;
		}
		result_data->grp_buffer = (char *)malloc(result_data->grp_buffer_size);
		memset(result_data->grp_buffer, 0, result_data->grp_buffer_size);

		status_code = _nss_ldap_getgrnam_r(request_data->name, &result_data->grp_result, result_data->grp_buffer, result_data->grp_buffer_size, error);
		status_code = __nss_compat_result(status_code, *error);
	} while ((status_code == NS_TRYAGAIN) && (result_data->grp_buffer_size <= GROUP_BUFFER_MAX_SIZE));

	return status_code;
}

enum nss_status 
ldap_getgrgid(struct getgrgid_data * request_data, struct group_result * result_data, int *error)
{
	enum nss_status status_code = NS_UNAVAIL;

	result_data->grp_buffer_size = GROUP_BUFFER_ALLOC_STEP;

	do {
		if (result_data->grp_buffer_size != GROUP_BUFFER_ALLOC_STEP) {
			free(result_data->grp_buffer);
			result_data->grp_buffer_size += GROUP_BUFFER_ALLOC_STEP;
		}
		result_data->grp_buffer = (char *)malloc(result_data->grp_buffer_size);
		memset(result_data->grp_buffer, 0, result_data->grp_buffer_size);

		status_code = _nss_ldap_getgrgid_r(request_data->gid, &result_data->grp_result, result_data->grp_buffer, result_data->grp_buffer_size, error);
		status_code = __nss_compat_result(status_code, *error);
	} while ((status_code == NS_TRYAGAIN) && (result_data->grp_buffer_size <= GROUP_BUFFER_MAX_SIZE));

	return status_code;
}

enum nss_status 
ldap_getpwnam(struct getpwnam_data * request_data, struct passwd_result * result_data, int *error, uid_t euid)
{
	enum nss_status status_code = NS_UNAVAIL;

	result_data->pwd_buffer_size = PASSWD_BUFFER_ALLOC_STEP;

	do {
		if (result_data->pwd_buffer_size != PASSWD_BUFFER_ALLOC_STEP) {
			free(result_data->pwd_buffer);
			result_data->pwd_buffer_size += PASSWD_BUFFER_ALLOC_STEP;
		}
		
		/* 
		 * here is the dirty hack with pw_class field of passwd structure
		 * it is mapped to the null byte of the buffer
		 * TODO:
		 * generally, it should be done in lookupd daemon
		 */		
		result_data->pwd_buffer = (char *)malloc(result_data->pwd_buffer_size+1);
		memset(result_data->pwd_buffer, 0, result_data->pwd_buffer_size+1);

		status_code = _nss_ldap_getpwnam_r(request_data->name, &result_data->pwd_result, result_data->pwd_buffer+1, result_data->pwd_buffer_size, error);
		status_code = __nss_compat_result(status_code, *error);

		if (status_code==NS_SUCCESS)
			result_data->pwd_result.pw_class=result_data->pwd_buffer;
	} while ((status_code == NS_TRYAGAIN) && (result_data->pwd_buffer_size <= PASSWD_BUFFER_MAX_SIZE));

	
	return status_code;
}

enum nss_status 
ldap_getpwuid(struct getpwuid_data * request_data, struct passwd_result * result_data, int *error, uid_t euid)
{
	enum nss_status status_code = NS_UNAVAIL;

	result_data->pwd_buffer_size = PASSWD_BUFFER_ALLOC_STEP;

	do {
		if (result_data->pwd_buffer_size != PASSWD_BUFFER_ALLOC_STEP) {
			free(result_data->pwd_buffer);
			result_data->pwd_buffer_size += PASSWD_BUFFER_ALLOC_STEP;
		}
		
		/* 
		 * here is the dirty hack with pw_class field of passwd structure
		 * it is mapped to the null byte of the buffer
		 * TODO:
		 * generally, it should be done in lookupd daemon
		 */
		result_data->pwd_buffer = (char *)malloc(result_data->pwd_buffer_size+1);
		memset(result_data->pwd_buffer, 0, result_data->pwd_buffer_size+1);

		status_code = _nss_ldap_getpwuid_r(request_data->uid, &result_data->pwd_result, result_data->pwd_buffer+1, result_data->pwd_buffer_size, error);
		status_code = __nss_compat_result(status_code, *error);

		if (status_code==NS_SUCCESS)
			result_data->pwd_result.pw_class=result_data->pwd_buffer;
	} while ((status_code == NS_TRYAGAIN) && (result_data->pwd_buffer_size <= PASSWD_BUFFER_MAX_SIZE));

	return status_code;
}

enum nss_status 
ldap_getgrent(struct getgrent_data * request_data, struct group_result * result_data, int *error)
{
	int cursor_position = 0;
	enum nss_status status_code = NS_UNAVAIL;

	if (request_data->cursor_size != 0)
		cursor_position = *((int *)request_data->cursor_buffer);
	else
		_nss_ldap_setgrent();

	result_data->grp_buffer_size = GROUP_BUFFER_ALLOC_STEP;
	do {
		if (result_data->grp_buffer_size != GROUP_BUFFER_ALLOC_STEP) {
			free(result_data->grp_buffer);
			result_data->grp_buffer_size += GROUP_BUFFER_ALLOC_STEP;
		}
		result_data->grp_buffer = (char *)malloc(result_data->grp_buffer_size);
		memset(result_data->grp_buffer, 0, result_data->grp_buffer_size);

		status_code = _nss_ldap_getgrent_r(&result_data->grp_result, result_data->grp_buffer, result_data->grp_buffer_size, error);
		status_code = __nss_compat_result(status_code, *error);
	} while ((status_code == NS_TRYAGAIN) && (result_data->grp_buffer_size <= GROUP_BUFFER_MAX_SIZE));

	if (status_code == NS_SUCCESS) {
		if (request_data->cursor_size == 0)
			request_data->cursor_buffer = (int *)malloc(sizeof(int));

		*((int *)request_data->cursor_buffer) = cursor_position + 1;
		request_data->cursor_size = sizeof(int);
	}
	return status_code;
}

enum nss_status 
ldap_getpwent(struct getpwent_data * request_data, struct passwd_result * result_data, int *error, uid_t euid)
{
	int cursor_position = 0;
	enum nss_status status_code = NS_UNAVAIL;

	if (request_data->cursor_size != 0)
		cursor_position = *((int *)request_data->cursor_buffer);
	else
		_nss_ldap_setpwent();

	result_data->pwd_buffer_size = PASSWD_BUFFER_ALLOC_STEP;
	do {
		if (result_data->pwd_buffer_size != PASSWD_BUFFER_ALLOC_STEP) {
			free(result_data->pwd_buffer);
			result_data->pwd_buffer_size += PASSWD_BUFFER_ALLOC_STEP;
		}
		
		/* 
		 * here is the dirty hack with pw_class field of passwd structure
		 * it is mapped to the null byte of the buffer
		 * TODO:
		 * generally, it should be done in lookupd daemon
		 */		
		result_data->pwd_buffer = (char *)malloc(result_data->pwd_buffer_size+1);
		memset(result_data->pwd_buffer, 0, result_data->pwd_buffer_size+1);

		status_code = _nss_ldap_getpwent_r(&result_data->pwd_result, result_data->pwd_buffer+1, result_data->pwd_buffer_size, error);
		status_code = __nss_compat_result(status_code, *error);
		
		if (status_code==NS_SUCCESS)
			result_data->pwd_result.pw_class=result_data->pwd_buffer;		
	} while ((status_code == NS_TRYAGAIN) && (result_data->pwd_buffer_size <= PASSWD_BUFFER_MAX_SIZE));

	if (status_code == NS_SUCCESS) {
		if (request_data->cursor_size == 0)
			request_data->cursor_buffer = (int *)malloc(sizeof(int));

		*((int *)request_data->cursor_buffer) = cursor_position + 1;
		request_data->cursor_size = sizeof(int);
	}
	return status_code;
}


syntax highlighted by Code2HTML, v. 0.9.1