/*
 * idmap_adex: Support for AD Forests
 *
 * Copyright (C) Gerald (Jerry) Carter 2006-2008
 *
 * 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 "idmap_adex.h"

#undef DBGC_CLASS
#define DBGC_CLASS DBGC_IDMAP

/**********************************************************************
**********************************************************************/

 char *find_attr_string(char **list, size_t num_lines, const char *substr)
{
	int i;
	int cmplen = strlen(substr);

	for (i = 0; i < num_lines; i++) {
		/* make sure to avoid substring matches like uid
		   and uidNumber */
		if ((StrnCaseCmp(list[i], substr, cmplen) == 0) &&
		    (list[i][cmplen] == '=')) {
			/* Don't return an empty string */
			if (list[i][cmplen + 1] != '\0')
				return &(list[i][cmplen + 1]);

			return NULL;
		}
	}

	return NULL;
}

/**********************************************************************
**********************************************************************/

 bool is_object_class(char **list, size_t num_lines, const char *substr)
{
	int i;

	for (i = 0; i < num_lines; i++) {
		if (strequal(list[i], substr)) {
			return true;
		}
	}

	return false;
}

/**********************************************************************
 Find out about the cell (e.g. use2307Attrs, etc...)
**********************************************************************/

 NTSTATUS cell_lookup_settings(struct likewise_cell * cell)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;

	/* Parameter check */

	if (!cell) {
		nt_status = NT_STATUS_INVALID_PARAMETER;
		BAIL_ON_NTSTATUS_ERROR(nt_status);
	}

	/* Only supporting Forest-wide, schema based searches */

	cell_set_flags(cell, LWCELL_FLAG_USE_RFC2307_ATTRS);
	cell_set_flags(cell, LWCELL_FLAG_SEARCH_FOREST);

	cell->provider = &ccp_unified;

	nt_status = NT_STATUS_OK;

done:
	if (!NT_STATUS_IS_OK(nt_status)) {
		DEBUG(1,("LWI: Failed to obtain cell settings (%s)\n",
			 nt_errstr(nt_status)));
	}

	return nt_status;
}


static NTSTATUS cell_lookup_forest(struct likewise_cell *c)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	struct gc_info *gc = NULL;

	if (!c) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
		nt_status = NT_STATUS_NO_MEMORY;
		BAIL_ON_NTSTATUS_ERROR(nt_status);
	}

	/* Query the rootDSE for the forest root naming conect first.
           Check that the a GC server for the forest has not already
	   been added */

	nt_status = gc_find_forest_root(gc, cell_dns_domain(c));
	BAIL_ON_NTSTATUS_ERROR(nt_status);

	c->forest_name = talloc_strdup(c, gc->forest_name);
	BAIL_ON_PTR_ERROR(c->forest_name, nt_status);

done:
	if (gc) {
		talloc_free(gc);
	}

	return nt_status;
}

/**********************************************************************
**********************************************************************/

 NTSTATUS cell_locate_membership(ADS_STRUCT * ads)
{
	ADS_STATUS status;
	char *domain_dn = ads_build_dn(lp_realm());
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	DOM_SID sid;
	struct likewise_cell *cell = NULL;

	/* In the Likewise plugin, I had to support the concept of cells
	   based on the machine's membership in an OU.  However, now I'll
	   just assume our membership in the forest cell */

	DEBUG(2, ("locate_cell_membership: Located membership "
		  "in cell \"%s\"\n", domain_dn));

	if ((cell = cell_new()) == NULL) {
		nt_status = NT_STATUS_NO_MEMORY;
		BAIL_ON_NTSTATUS_ERROR(nt_status);
	}

	status = ads_domain_sid(ads, &sid);
	if (!ADS_ERR_OK(status)) {
		DEBUG(3,("locate_cell_membership: Failed to find "
			 "domain SID for %s\n", domain_dn));
	}

	/* save the SID and search base for our domain */

	cell_set_dns_domain(cell, lp_realm());
	cell_set_connection(cell, ads);
	cell_set_dn(cell, domain_dn);
	cell_set_domain_sid(cell, &sid);

	/* Now save our forest root */

	cell_lookup_forest(cell);

	/* Add the cell to the list */

	if (!cell_list_add(cell)) {
		nt_status = NT_STATUS_INSUFFICIENT_RESOURCES;
		BAIL_ON_NTSTATUS_ERROR(nt_status);
	}

	/* Done! */
	nt_status = NT_STATUS_OK;

done:
	if (!NT_STATUS_IS_OK(nt_status)) {
		DEBUG(0,("LWI: Failed to locate cell membership (%s)\n",
			 nt_errstr(nt_status)));
	}

	SAFE_FREE(domain_dn);

	return nt_status;
}

/*********************************************************************
 ********************************************************************/

 int min_id_value(void)
{
	int id_val;

	id_val = lp_parm_int(-1, "lwidentity", "min_id_value", MIN_ID_VALUE);

	/* Still don't let it go below 50 */

	return MAX(50, id_val);
}

/********************************************************************
 *******************************************************************/

 char *cell_dn_to_dns(const char *dn)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	char *domain = NULL;
	char *dns_name = NULL;
	const char *tmp_dn;
	char *buffer = NULL;
	TALLOC_CTX *frame = talloc_stackframe();

	if (!dn || !*dn) {
		goto done;
	}

	tmp_dn = talloc_strdup(frame, dn);
	BAIL_ON_PTR_ERROR(tmp_dn, nt_status);

	while (next_token_talloc(frame, &tmp_dn, &buffer, ",")) {

		/* skip everything up the where DC=... begins */
		if (StrnCaseCmp(buffer, "DC=", 3) != 0)
			continue;

		if (!domain) {
			domain = talloc_strdup(frame, &buffer[3]);
		} else {
			domain = talloc_asprintf_append(domain, ".%s",
							&buffer[3]);
		}
		BAIL_ON_PTR_ERROR(domain, nt_status);
	}

	dns_name = SMB_STRDUP(domain);
	BAIL_ON_PTR_ERROR(dns_name, nt_status);

	nt_status = NT_STATUS_OK;

done:
	PRINT_NTSTATUS_ERROR(nt_status, "cell_dn_to_dns", 1);

	talloc_destroy(frame);

	return dns_name;
}

/*********************************************************************
 ********************************************************************/

 NTSTATUS get_sid_type(ADS_STRUCT *ads,
		       LDAPMessage *msg,
		       enum lsa_SidType *type)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	uint32_t atype;

	if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
		nt_status = NT_STATUS_INVALID_USER_BUFFER;
		BAIL_ON_NTSTATUS_ERROR(nt_status);
	}

	switch (atype &0xF0000000) {
	case ATYPE_SECURITY_GLOBAL_GROUP:
		*type = SID_NAME_DOM_GRP;
		break;
	case ATYPE_SECURITY_LOCAL_GROUP:
		*type = SID_NAME_ALIAS;
		break;
	case ATYPE_NORMAL_ACCOUNT:
	case ATYPE_WORKSTATION_TRUST:
	case ATYPE_INTERDOMAIN_TRUST:
		*type = SID_NAME_USER;
		break;
	default:
		*type = SID_NAME_USE_NONE;
		nt_status = NT_STATUS_INVALID_ACCOUNT_NAME;
		BAIL_ON_NTSTATUS_ERROR(nt_status);
	}

	nt_status = NT_STATUS_OK;

done:
	return nt_status;
}